Linux C 编程——多线程
线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。与多进程相比,多进程具有多进程不具备的一些优点,其最重要的是:对于多线程来说,其能够比多进程更加节省资源。
1、线程创建
在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。
在Linux中,通过函数pthread_create()
函数实现线程的创建:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
其中:
- thread表示的是一个pthread_t类型的指针;
- attr用于指定线程的一些属性;
- start_routine表示的是一个函数指针,该函数是线程调用函数;
- arg表示的是传递给线程调用函数的参数。
当线程创建成功时,函数pthread_create()
返回0,若返回值不为0则表示创建线程失败。对于线程的属性,则在结构体pthread_attr_t
中定义。
线程创建的过程如下所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
printf("this is a new thread, thread ID is %un", newthid);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
printf("main thread, ID is %un", pthread_self());
for (int i = 0; i < num_thread; i++){
if (pthread_create(&pt[i], NULL, thread, NULL) != 0){
printf("thread create failed!n");
return 1;
}
}
sleep(2);
free(pt);
return 0;
}
在上述代码中,使用到了pthread_self()
函数,该函数的作用是获取本线程的线程ID。在主函数中的sleep()
用于将主进程处于等待状态,以让线程执行完成。最终的执行效果如下所示:
那么,如何利用arg向子线程传递参数呢?其具体的实现如下所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%dn", newthid, num);
return NULL;
}
int main(){
//pthread_t thid;
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %un", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!n");
return 1;
}
}
sleep(2);
free(pt);
free(id);
return 0;
}
其最终的执行效果如下图所示:
如果在主进程提前结束,会出现什么情况呢?如下述的代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%dn", newthid, num);
sleep(2);
printf("thread %u is done!n", newthid);
return NULL;
}
int main(){
//pthread_t thid;
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %un", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!n");
return 1;
}
}
//sleep(2);
free(pt);
free(id);
return 0;
}
此时,主进程提前结束,进程会将资源回收,此时,线程都将退出执行,运行结果如下所示:
2、线程挂起
在上述的实现过程中,为了使得主线程能够等待每一个子线程执行完成后再退出,使用了free()
函数,在Linux的多线程中,也可以使用pthread_join()
函数用于等待其他线程,函数的具体形式为:
int pthread_join(pthread_t thread, void **retval);
函数pthread_join()
用来等待一个线程的结束,其调用这将被挂起。
一个线程仅允许一个线程使用
pthread_join()
等待它的终止。
如需要在主线程中等待每一个子线程的结束,如下述代码所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%dn", newthid, num);
free(3);
printf("thread %u is donen", newthid);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %un", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!n");
return 1;
}
}
for (int i = 0; i < num_thread; i++){
pthread_join(pt[i], NULL);
}
free(pt);
free(id);
return 0;
}
最终的执行效果如下所示:
注:在编译的时候需要链接libpthread.a:
g++ xx.cc -lpthread -o xx
- 比特币勒索病毒肆虐,腾讯云安全专家给你支招
- HTML5 直播协议之 WebSocket 和 MSE
- IoC在ASP.NET Web API中的应用
- 跟鹅厂老司机学技术之一:“遇见” Kotlin
- 简单的 H5 视频推流解决方案
- 来腾讯云开发者实验室学习.NET
- 跨域资源共享(CORS)在ASP.NET Web API中是如何实现的?
- 使用腾讯云 GPU 学习深度学习系列之六:物体的识别与定位
- npm5 新版功能特性解析及与 yarn 评测对比
- H5直播避坑指南
- 龙门阵之 DevOps 门外汉须知
- 全面进阶 H5 直播(上)
- 这年头,不会斗图都毕不了业?
- 龙妈也会死?《权力的游戏》 AI 算法预测丹妮女王危在旦夕
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 我用 OpenGL 实现了那些年流行的相机滤镜
- 使用minikube安装kubernetes和dashboard
- FFmpeg + OpenGLES 实现视频解码播放和视频滤镜
- 行为型设计模式:模板模式
- 容器中的隔离与限制:namespace和cgroups
- 行为型设计模式:策略模式
- redis实战第十二篇 redis cluster请求重定向
- 行为型设计模式:责任链模式以及mybatis中的责任链
- java中BigDecimal使用和注意事项
- redis实战第十一篇 redis cluster添加密码
- Linux下docker制作springboot应用镜像
- 行为型设计模式:备忘录模式
- 行为型设计模式:访问者模式
- redis实战第十篇 集群收缩
- redis实战第九篇 集群扩容自动迁移槽(redis-cli)