Linux多线程并发运行原理+代码例程详解
发布日期:2021-05-08 17:16:03 浏览次数:24 分类:精选文章

本文共 11474 字,大约阅读时间需要 38 分钟。

文章目录

线程创建和退出

原理

在这里插入图片描述

代码

交叉编译

#include 
#include
#include
/*thread1*/void thread1(void){ int i = 0; for(i = 0; i < 6; i++) { printf("This is a pthread1. \n"); if(i == 2){ pthread_exit(0); } sleep(1); }}/*thread2*/void thread2(void){ int i; for(i = 0; i < 3; i++){ printf("This is a pthread2. \n"); } pthread_exit(0);}/*main*/int main(void){ pthread_t id1, id2; int i,ret; /*create thread1*/ ret = pthread_create(&id1, NULL, (void *) thread1, NULL); if(ret != 0){ printf("Create pthread1 error!\n"); exit(1); } /*create thread2*/ ret = pthread_create(&id2, NULL, (void *) thread2, NULL); if(ret != 0) { printf("Create pthread2 error!\n"); exit(1); } /*wait for thread to end*/ pthread_join(id1, NULL); pthread_join(id2, NULL); exit(0); return 0;}

执行

$CC thread_test.c

结果报错

在这里插入图片描述
出现编译错误undefined reference to ‘pthread_create’。由于pthread库不是标准linux库, 所以出错。 找不到函数库,通过静态调用,-lpthread,就可以编译通过

$CC thread_test.c -o thread_test.0 -lpthread

在这里插入图片描述

然后通过scp命令发送可执行文件到开发板

scp thread_test root@192.168.0.232:/tmp

在这里插入图片描述

结果

在开发板中执行

在这里插入图片描述

线程属性修改

原理

在这里插入图片描述

代码

#include 
#include
#include
#include
/*thread1*/void thread1(void){ int i = 0; for(i = 0; i < 6; i++) { printf("This is a pthread1. \n"); if(i == 2){ pthread_exit(0); } sleep(1); }}/*thread2*/void thread2(void){ int i; while(1){ for(i = 0; i < 3; i++){ printf("This is a pthread2. \n"); sleep(1); } } /* void pthread_exit(void *retval) * Retval:pthread_exit()调用者线程的返回值,可由其他函数如pthread_join 来检索获取 */ pthread_exit(0);}/*main*/int main(void){ pthread_t id1, id2; int i,ret; pthread_attr_t attr; /*初始化线程 */ pthread_attr_init(&attr); /*设置线程绑定属性*/ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); /*设置线程分离属性*/ pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); /* create thread1 创建线程 * int pthread_create ((pthread_t *thread, pthread_attr_t *attr,void *(*start_routine)(void *), void *arg)) * thread:线程标识符 * attr:线程属性设置 * start_routine:线程函数的起始地址 * arg:传递给start_routine的参数 * return:成功:0 出错:-1 */ ret=pthread_create(&id1,&attr,(void *) thread1,NULL); if(ret != 0){ printf("Create pthread1 error!\n"); exit(1); } /*create thread2*/ ret = pthread_create(&id2, NULL, (void *) thread2, NULL); if(ret != 0) { printf("Create pthread2 error!\n"); exit(1); } /*wait for thread to end*/ /* int pthread_join ((pthread_t th, void **thread_return)) * th:等待线程的标识符 * thread_return:用户定义的指针,用来存储被等待线程的返回值(不为NULL时) * return:成功:0 出错:-1 */ // pthread_join(id1, NULL); // pthread_join将当前线程挂起,等待线程的结束 pthread_join(id2, NULL); // exit(0); // 所有线程都终止 return 0;}

这次不放在嵌入式开发板上运行了,所以就用gcc编译了

gcc thread_test2.c -o thread_test2 -lpthread

结果

直接在wsl中运行编译好的可执行文件thread_test2

在这里插入图片描述
因为有个while死循环的存在,所以程序就会一直运行下去,我们ctrl+c来结束打印,然后输入free

使用“free”命令查看内存使用情况

这是程序运行之前的内存使用情况uesd

在这里插入图片描述
这是程序运行之后的内存使用情况used
在这里插入图片描述

互斥锁

原理

在这里插入图片描述

代码

/* * 使用互斥锁来实现对变量lock_var 的加一、打印操作 */#include 
#include
#include
#include
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 创建快速互斥锁int lock_var;time_t end_time;void pthread1(void *arg);void pthread2(void *arg);int main(int argc, char *argv[]){ pthread_t id1,id2; pthread_t mon_th_id; int ret; end_time = time(NULL) + 10; /* 互斥锁初始化 * int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) * Mutex:互斥锁 * Mutexattr:PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁;PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁;PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁 * return:成功:0 出错:-1 */ pthread_mutex_init(&mutex, NULL); /*创建两个线程*/ ret = pthread_create(&id1, NULL, (void *)pthread1, NULL); if(ret != 0){ perror("pthread cread1"); } ret = pthread_create(&id2, NULL, (void *)pthread2, NULL); if(ret != 0){ perror("pthread cread2"); } pthread_join(id1, NULL); pthread_join(id2, NULL); exit(0);}void pthread1(void *arg){ int i; while(time(NULL) < end_time){ /* * int pthread_mutex_lock(pthread_mutex_t *mutex,) int pthread_mutex_trylock(pthread_mutex_t *mutex,) int pthread_mutex_unlock(pthread_mutex_t *mutex,) int pthread_mutex_destroy(pthread_mutex_t *mutex,) * Mutex:互斥锁 * return:成功:0 出错:-1 */ /*互斥上锁*/ if(pthread_mutex_lock(&mutex) != 0){ perror("pthread_mutex_lock"); }else{ printf("pthread1:pthread1 lock the variable\n"); } for(i = 0; i < 0; i++){ sleep(1); lock_var++; } /*互斥锁解锁*/ if(pthread_mutex_unlock(&mutex) != 0){ perror("pthread_mutex_unlock\n"); }else{ printf("pthread1:pthread1 unlock the variable\n"); } sleep(1); }}void pthread2(void *arg){ int nolock = 0; int ret; while(time(NULL) < end_time){ /*测试互斥锁*/ ret = pthread_mutex_trylock(&mutex); if(ret == EBUSY){ printf("pthread2:the variable is locked by pthread1\n"); }else{ if(ret != 0){ perror("pthread_mutex_trylock"); exit(1); }else{ printf("pthread2:pthread2 got lock. The variable is %d\n", lock_var); } /*互斥锁解锁*/ if(pthread_mutex_unlock(&mutex) != 0){ perror("pthread_mutex_unlock"); }else{ printf("pthread2:pthread2 unlock the variable\n"); } } sleep(3); }}

代码是什么意思呢?

在这里插入图片描述
两个线程并发,线程一定时while循环,锁定时候线程二检测到上锁然后不运行服务函数,检测到解锁后运行服务函数输出

编译代码

在这里插入图片描述

结果

运行可执行文件

在这里插入图片描述

信号量线程控制原理

在这里插入图片描述

使用信号量线程互斥

使用同一个信号量sem,sem初始值设置为0,进程一执行P操作,sem-1=-1,所以该进程就将阻塞直到信号量sem大于等于0为止。

代码

/* * 信号量线程控制 */#include 
#include
#include
#include
#include
#include
#include
#include
int lock_var;time_t end_time;sem_t sem;void pthread1(void *arg);void pthread2(void *arg);int main(int argc, char *argv[]){ pthread_t id1, id2; pthread_t mon_th_id; int ret; end_time = time(NULL) + 30; /*初始化信号量为1*/ ret = sem_init(&sem, 0, 1); if(ret != 0){ perror("sem_init"); } /*创建两个线程*/ ret = pthread_create(&id1, NULL, (void *)pthread1, NULL); if(ret != 0){ perror("pthread cread1"); } ret = pthread_create(&id2, NULL, (void *)pthread2, NULL); if(ret != 0){ perror("pthread cread2"); } pthread_join(id1, NULL); pthread_join(id2, NULL); exit(0);}void pthread1(void *arg){ int i; while(time(NULL) < end_time){ /*信号量减一,p操作*/ sem_wait(&sem); for(i = 0; i < 2; i++){ sleep(1); lock_var++; printf("lock_var = %d\n", lock_var); } printf("pthread1:lock_var = %d\n", lock_var); /*信号量加一,v操作*/ sem_post(&sem); sleep(1); }}void pthread2(void *arg){ int nolock = 0; int ret; while(time(NULL) < end_time){ /*信号量减一,p操作*/ sem_wait(&sem); printf("pthread2:pthread1 got lock; lock_var = %d\n", lock_var); /*信号量加一,v操作*/ sem_post(&sem); sleep(3); }}

在这里插入图片描述

结果

运行结果如图所示

在这里插入图片描述

在输出的结果中,可以很清晰的看到,进程一执行了P操作后,线程二就被阻塞了不能执行,等待线程一执行完for循环过了2s之后,线程一执行V操作,sem=0,然后再等待一秒钟

sem现在大于等于0,所以该进程具有公共资源的访问权限,线程二可以执行V操作修改sem的值了,sem=-1,线程之间又发生了互斥,线程二执行输出,然后sleep 3秒钟。

使用信号量线程同步

在这里插入图片描述

代码

/* * 通过两个信号量来实现两个线程间的同步 * 程序先运行线程二,再运行线程一 */#include 
#include
#include
#include
#include
#include
#include
int lock_var;time_t end_time;sem_t sem1, sem2;void pthread1(void *arg);void pthread2(void *arg);int main(int argc, char *argv[]){ pthread_t id1, id2; pthread_t mon_th_id; int ret; end_time = time(NULL) + 30; /*初始化两个信号量,一个信号量为1,一个信号量为0*/ ret = sem_init(&sem1, 0, 1); ret = sem_init(&sem2, 0, 0); if(ret != 0){ perror("sem_init"); } /*创建两个线程*/ ret = pthread_create(&id1, NULL, (void *)pthread1, NULL); if(ret != 0){ perror("pthread cread1"); } ret = pthread_create(&id2, NULL, (void *)pthread2, NULL); if(ret != 0){ perror("pthread cread2"); } pthread_join(id1, NULL); pthread_join(id2, NULL); exit(0);}void pthread1(void *arg){ int i; while(time(NULL) < end_time){ /*P操作信号量sem2 = 0 信号量-1*/ sem_wait(&sem2);//现在sem2=-1,该线程阻塞,直到sem2的值大于等于零为止,也就是进程二执行V操作sem2=0 for(i = 0; i < 2; i++){ sleep(1); lock_var++; printf("lock_var = %d\n", lock_var); } printf("pthread1:lock_var = %d\n", lock_var); /*V 操作信号量sem1 = 0 +1*/ sem_post(&sem1);//sem1 = 1 sleep(1); }}void pthread2(void *arg){ int nolock = 0; int ret; while(time(NULL) < end_time){ /*P 操作信号量sem1 = 1 信号量-1*/ sem_wait(&sem1);//现在sem1=0 该线程具有公共资源的访问权限,执行 printf("pthread2:pthread1 got lock; lock_var = %d\n", lock_var); /*V 操作信号量sem2 = -1 +1*/ sem_post(&sem2);//现在sem2=0,所以线程一就可以运行了 sleep(3); }}

结果

从以下结果中可以看出,该程序确实实现了先运行线程二,再运行线程一。

在这里插入图片描述

小结分析

对于线程一和线程二都在3s 的一个周期内进行并行的操作,因为有两个信号量,所以可以分别控制

在这里插入图片描述

在第一个时间中,sem2 = -1,sem1 = 0;所以线程二可以执行,线程一此时被阻塞, 需要等待sem2 >= 0 的状态。线程二执行了自己的输出pthread2:pthread1 got lock; lock_var = 0,然后执行V操作,sem2 = 0,这时候线程一满足了执行的条件,执行设定的输出lock_var = 1。这时候线程二也在运行,执行sleep(3),也就是延迟3s,只是这个过程没有了其他的输出。

在第二个时间中,因为满足了sem2 >= 0 的条件,所以继续执行lock_var = 2,并且结束了for循环,执行输出pthread1:lock_var = 2,而于此同时线程二依然在sleep中。

在第三个时间中,线程一执行了V操作,sem1 = 1,时间也来到了第三秒,一个时间周期完成。

“生产者消费者”实验

问题分析

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

代码

/* * 有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲   区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时页必须等   待。 * 这里使用3个信号量,其中两个信号量avail和full分别用于解决生产者和消费者线程之   间的同步问题,mutex是用于这两个线程之间的互斥问题。其中avail初始化为N(有界缓冲    区的空单元数),mutex 初始化为1,full初始化为0。 */#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO "myfifo"#define N 5int lock_var = 0;int fd;char buf_r[100];sem_t mutex, full, avail;time_t end_time;void productor(void *arg);void consumer(void *arg);void pthread1(void *arg);void pthread2(void *arg);/*主函数*/int main(int argc, char *argv[]){ pthread_t id1, id2; pthread_t mon_th_id; int ret; end_time = time(NULL) + 30; /*创建有名管道*/ if((mkfifo(FIFO, O_CREAT|O_EXCL) < 0) && (errno != EEXIST)){ printf("cannot create fifoserver\n"); } printf("Preparing for reading bytes...\n"); memset(buf_r, 0, sizeof(buf_r)); /*打开管道*/ fd = open(FIFO, O_RDWR|O_NONBLOCK, 0); // 新建文件myfifo if(fd == -1){ perror("open"); exit(1); } /*初始化互斥信号量为1*/ ret = sem_init(&mutex, 0, 1); /*初始化avail信号量为N*/ ret = sem_init(&avail, 0, N); /*初始化full信号量为0*/ ret = sem_init(&full, 0, 0); if(ret != 0){ perror("sem_init"); } /*创建两个线程*/ ret = pthread_create(&id1, NULL, (void *)productor, NULL); if(ret != 0){ perror("pthread cread1"); } ret = pthread_create(&id2, NULL, (void *)consumer, NULL); if(ret != 0){ perror("pthread cread2"); } pthread_join(id1, NULL); pthread_join(id2, NULL); exit(0);}/*生产者线程*/void productor(void *arg){ int i, nwrite; while(time(NULL) < end_time){ /*P操作信号量avail和mutex*/ sem_wait(&avail); // avail= N-1 sem_wait(&mutex); // mutex= 1-1 =0 /*生产者写入数据*/ if((nwrite = write(fd, "hello", 5)) == -1){ // 字符数据“hello”写入到fd文件中 if(errno == EAGAIN){ printf("The FIFO has not been read yet. Please try later\n"); } }else{ printf("write hello to the FIFO\n"); } /*V操作信号量full和mutex*/ sem_post(&full); // full = -1+1 = 0 消费者线程可以运行了 sem_post(&mutex); // mutex = 0+1 = 1 sleep(1); }}/*消费者线程*/void consumer(void *arg){ int nolock = 0; int ret, nread; while(time(NULL) < end_time){ /*P操作信号量full和mutex*/ sem_wait(&full); // full = 0-1 = -1 阻塞等待生产者写入完成 full>=0 sem_wait(&mutex); // mutex = 1-1 = 0 memset(buf_r, 0, sizeof(buf_r)); // buf_r 置零 if((nread = read(fd, buf_r, 100)) == -1){ // 读取fd中消费者写入的数据到buf_r中 if(errno == EAGAIN){ printf("no data yet\n"); } } printf("read %s from FIFO\n", buf_r); // 输出buf_r /*V操作信号量avail和mutex*/ sem_post(&avail); // avail = N sem_post(&mutex); // mutex = 0+1 = 1 sleep(1); }}

结果分析

编译之后执行,得到了预期的结果

在这里插入图片描述

上一篇:Linux开发环境搭建关键的库文件搜索调用(静态库)
下一篇:通过J-Link下载HEX文件

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月17日 09时57分33秒