linux多线程编程——同步与互斥
一、 为什么要用多线程技术?
1、避免阻塞,大家知道,单个进程只有一个主线程,当主线程阻塞的时候,整个进程也就阻塞了,无法再去做其它的一些功能了。
2、避免CPU空转,应用程序经常会涉及到RPC,数据库访问,磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应时,CPU却不能去处理新的请求,导致这种单线程的应用程序性能很差。
3、提升效率,一个进程要独立拥有4GB的虚拟地址空间,而多个线程可以共享同一地址空间,线程的切换比进程的切换要快得多。
二、 如何使用多线程技术进行编程?
下面给出个多线程程序,一个最简单的模拟售票系统,代码如下:
- #include <stdio.h>
- #include <pthread.h>
- void *ticketsell1(void *);
- void *ticketsell2(void *);
- int tickets = 20;
- int main()
- {
- pthread_t id1,id2;
- int error;
- error = pthread_create(&id1, NULL, ticketsell1, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- error = pthread_create(&id2, NULL, ticketsell2, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- pthread_join(id1,NULL);
- pthread_join(id2,NULL);
- return 0;
- }
- void *ticketsell1(void *arg)
- {
- while(1)
- {
- if(tickets > 0)
- {
- // usleep(1000);
- printf("ticketse1 sells ticket:%d\n",tickets--);
- }
- else
- {
- break;
- }
- }
- return (void *)0;
- }
- void *ticketsell2(void *arg)
- {
- while(1)
- {
- if(tickets > 0)
- {
- // usleep(1000);
- printf("ticketse2 sells ticket:%d\n",tickets--);
- }
- else
- {
- break;
- }
- }
- return (void *)0;
- }
执行结果如下:
- fs@ubuntu:~/qiang/mthread$ ./mthread1
- ticketse2 sells ticket:20
- ticketse2 sells ticket:19
- ticketse2 sells ticket:18
- ticketse2 sells ticket:17
- ticketse2 sells ticket:16
- ticketse2 sells ticket:15
- ticketse2 sells ticket:14
- ticketse2 sells ticket:13
- ticketse2 sells ticket:12
- ticketse2 sells ticket:11
- ticketse2 sells ticket:10
- ticketse2 sells ticket:9
- ticketse2 sells ticket:8
- ticketse2 sells ticket:7
- ticketse2 sells ticket:6
- ticketse2 sells ticket:4
- ticketse2 sells ticket:3
- ticketse2 sells ticket:2
- ticketse2 sells ticket:1
- ticketse1 sells ticket:5
看到结果,我们发现时能正常卖票的,一部分连续是sel2,另一部分是ticketsel1;
此时,其实存在一个隐含的问题,就是线程间的切换,在单CPU系统中,CPU是有时间片时间,时间片到了,就要执行其它的线程,假设thread1执行到if里面,但在printf执行前发生了线程切换,那么会发生什么呢?我们在这里用usleep函数(放开程序中的usleep注释行)进行强制模拟切换;
我们看看结果:
- fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread
- fs@ubuntu:~/qiang/mthread$ ./mthread1
- ticketse2 sells ticket:20
- ticketse1 sells ticket:19
- ticketse2 sells ticket:18
- ticketse1 sells ticket:17
- ticketse2 sells ticket:16
- ticketse1 sells ticket:15
- ticketse2 sells ticket:14
- ticketse1 sells ticket:13
- ticketse2 sells ticket:12
- ticketse1 sells ticket:11
- ticketse2 sells ticket:10
- ticketse1 sells ticket:9
- ticketse2 sells ticket:8
- ticketse1 sells ticket:7
- ticketse2 sells ticket:6
- ticketse1 sells ticket:5
- ticketse2 sells ticket:4
- ticketse1 sells ticket:3
- ticketse1 sells ticket:2
- ticketse2 sells ticket:1
- ticketse1 sells ticket:0
- fs@ubuntu:~/qiang/mthread$
运行程序发现竟然有0号票被卖出了,这显然是错误的!当thread1的if里面发生线程切换时,thread2得到运行,把最后一张票卖了,此时thread1恢复运行,结果卖出了0号票,这里我们需要的是火车票的票数数据对于所有线程而言是同步的,所以就要用到线程同步技术了。
三、 使用多线程的同步与互斥
1、多线程的同步方式有很多种,例如互斥锁,条件变量,信号量,读写锁。先看看互斥锁如何解决多线程之间的同步问题。程序用互斥锁后如下:
- #include <stdio.h>
- #include <pthread.h>
- void *ticketsell1(void *);
- void *ticketsell2(void *);
- int tickets = 20;
- pthread_mutex_t mutex;
- int main()
- {
- pthread_t id1,id2;
- pthread_mutex_init(&mutex, NULL);//
- int error;
- error = pthread_create(&id1, NULL, ticketsell1, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- error = pthread_create(&id2, NULL, ticketsell2, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- pthread_join(id1,NULL);
- pthread_join(id2,NULL);
- return 0;
- }
- void *ticketsell1(void *arg)
- {
- while(1)
- {
- pthread_mutex_lock(&mutex);//给互斥量上锁
- if(tickets > 0)
- {
- usleep(1000);
- printf("ticketse1 sells ticket:%d\n",tickets--);
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- }
- else
- {
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- break;
- }
- pthread_yield();//线程调度函数,使每个线程都有执行机会
- }
- return (void *)0;
- }
- void *ticketsell2(void *arg)
- {
- while(1)
- {
- pthread_mutex_lock(&mutex);//给互斥量上锁
- if(tickets > 0)
- {
- usleep(1000);
- printf("ticketse2 sells ticket:%d\n",tickets--);
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- }
- else
- {
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- break;
- }
- pthread_yield();//线程调度函数,是两个线程都有执行机会
- }
- return (void *)0;
- }
执行结果如下:
- fs@ubuntu:~/qiang/mthread$ vi mthread1.c
- fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread
- fs@ubuntu:~/qiang/mthread$ ./mthread1
- ticketse2 sells ticket:20
- ticketse1 sells ticket:19
- ticketse2 sells ticket:18
- ticketse1 sells ticket:17
- ticketse2 sells ticket:16
- ticketse1 sells ticket:15
- ticketse2 sells ticket:14
- ticketse1 sells ticket:13
- ticketse2 sells ticket:12
- ticketse1 sells ticket:11
- ticketse2 sells ticket:10
- ticketse1 sells ticket:9
- ticketse2 sells ticket:8
- ticketse1 sells ticket:7
- ticketse2 sells ticket:6
- ticketse1 sells ticket:5
- ticketse2 sells ticket:4
- ticketse1 sells ticket:3
- ticketse2 sells ticket:2
- ticketse1 sells ticket:1
2、再看看用信号量来解决多线程的同步问题,程序代码如下:
- #include <stdio.h>
- #include <pthread.h>
- #include <semaphore.h>
- void *ticketsell1(void *);
- void *ticketsell2(void *);
- int tickets = 20;
- sem_t mutex,full;
- int main()
- {
- pthread_t id1,id2;
- int error;
- int ret;
- ret = sem_init(&mutex, 0 ,1);//初始化mutex信号量为1
- ret += sem_init(&full, 0 ,0);//初始化full信号量为0
- if(ret != 0)
- {
- printf("sem_init fails!\n");
- }
- error = pthread_create(&id1, NULL, ticketsell1, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- error = pthread_create(&id2, NULL, ticketsell2, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- pthread_join(id1,NULL);
- pthread_join(id2,NULL);
- return 0;
- }
- void *ticketsell1(void *arg)
- {
- while(1)
- {
- sem_wait(&mutex);//mutex信号量进行P操作
- if(tickets > 0)
- {
- usleep(1000);
- printf("ticketse1 sells ticket:%d\n",tickets--);
- sem_post(&full);//full信号量进行V操作
- }
- else
- {
- sem_post(&full);//full信号量进行V操作
- break;
- }
- }
- return (void *)0;
- }
- void *ticketsell2(void *arg)
- {
- while(1)
- {
- sem_wait(&full);//full信号量进行P操作
- if(tickets > 0)
- {
- usleep(1000);
- printf("ticketse2 sells ticket:%d\n",tickets--);
- sem_post(&mutex);//mutex信号量进行V操作
- }
- else
- {
- sem_post(&mutex);//mutex信号量进行V操作
- break;
- }
- }
- return (void *)0;
- }
执行结果:
- fs@ubuntu:~/qiang/mthread$ vi mthread1.c
- fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread
- fs@ubuntu:~/qiang/mthread$ ./mthread1
- ticketse1 sells ticket:20
- ticketse2 sells ticket:19
- ticketse1 sells ticket:18
- ticketse2 sells ticket:17
- ticketse1 sells ticket:16
- ticketse2 sells ticket:15
- ticketse1 sells ticket:14
- ticketse2 sells ticket:13
- ticketse1 sells ticket:12
- ticketse2 sells ticket:11
- ticketse1 sells ticket:10
- ticketse2 sells ticket:9
- ticketse1 sells ticket:8
- ticketse2 sells ticket:7
- ticketse1 sells ticket:6
- ticketse2 sells ticket:5
- ticketse1 sells ticket:4
- ticketse2 sells ticket:3
- ticketse1 sells ticket:2
- ticketse2 sells ticket:1
- fs@ubuntu:~/qiang/mthread$
上面的sem_init函数用来初始化两个信号量的初始化值,这里一个设为1,一个设为0,sem_wait类似于P操作,让信号量减1,如果小于结果小于0,线程阻塞,否则线程继续执行,sem_post类似于V操作,提升信号量的值,加1,通过这两个信号量之间的互相“救对方”,就可以实现这两个线程的同步执行。
我们编译运行以上程序,发现两个售票点交替卖票,两个纯程依次得到机会执行,并且不会有0号票卖出,实现了同步。
3、我们再用条件变量来解决同步问题,一般条件变量需要结合互斥量一起使用,代码如下
- #include <stdio.h>
- #include <pthread.h>
- #include <semaphore.h>
- void *ticketsell1(void *);
- void *ticketsell2(void *);
- int tickets = 20;
- pthread_mutex_t mutex;
- pthread_cond_t qready = PTHREAD_COND_INITIALIZER;//静态初始化条件变量;
- int main()
- {
- pthread_t id1,id2;
- pthread_mutex_init(&mutex, NULL);
- int error;
- error = pthread_create(&id1, NULL, ticketsell1, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- error = pthread_create(&id2, NULL, ticketsell2, NULL);
- if(error != 0)
- {
- printf("pthread is not created!\n");
- return -1;
- }
- pthread_join(id1,NULL);
- pthread_join(id2,NULL);
- return 0;
- }
- void *ticketsell1(void *arg)
- {
- pthread_mutex_lock(&mutex);
- while(tickets > 0)
- {
- if(tickets%2 == 1)
- {
- usleep(1000);
- printf("ticketse1 sells ticket:%d\n",tickets--);
- pthread_cond_signal(&qready);//条件改变,发送信号,通知ticketse2
- }
- else
- {
- pthread_cond_wait(&qready,&mutex);//解开Mutex,并等待qready改变
- }
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- }
- return (void *)0;
- }
- void *ticketsell2(void *arg)
- {
- pthread_mutex_lock(&mutex);
- while(tickets > 0)
- {
- if(tickets%2 == 0)
- {
- usleep(1000);
- printf("ticketse2 sells ticket:%d\n",tickets--);
- pthread_cond_signal(&qready);//条件改变,发送信号,通知ticketse1
- }
- else
- {
- pthread_cond_wait(&qready,&mutex);//解开mutex,并等待qready改变
- }
- pthread_mutex_unlock(&mutex);//给互斥量解锁
- }
- return (void *)0;
- }
执行结果如下:
- fs@ubuntu:~/qiang/mthread$ vi mthread1.c
- fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread
- fs@ubuntu:~/qiang/mthread$ ./mthread1
- ticketse2 sells ticket:20
- ticketse1 sells ticket:19
- ticketse2 sells ticket:18
- ticketse1 sells ticket:17
- ticketse2 sells ticket:16
- ticketse1 sells ticket:15
- ticketse2 sells ticket:14
- ticketse1 sells ticket:13
- ticketse2 sells ticket:12
- ticketse1 sells ticket:11
- ticketse2 sells ticket:10
- ticketse1 sells ticket:9
- ticketse2 sells ticket:8
- ticketse1 sells ticket:7
- ticketse2 sells ticket:6
- ticketse1 sells ticket:5
- ticketse2 sells ticket:4
- ticketse1 sells ticket:3
- ticketse2 sells ticket:2
- ticketse1 sells ticket:1
- fs@ubuntu:~/qiang/mthread$
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件变量发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步.
函数pthread_cond_wait使线程阻塞在一个条件变量上,而函数pthread_cond_signal是用来释放被阻塞在条件变量上的一个线程。但是要注意的是,条件变量只是起到阻塞和唤醒线程的作用,具体的判断条件还需用户给出,我这里给出的是tickets是否是偶数这个条件。
linux多线程编程——同步与互斥相关推荐
- Linux多线程编程---线程间同步(互斥锁、条件变量、信号量和读写锁)
本篇博文转自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了 ...
- 多线程编程(3): 使用互斥锁同步线程
python多线程编程(3): 使用互斥锁同步线程 问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据n ...
- Linux多线程编程-互斥锁
互斥锁 多线程编程中,(多线程编程)可以用互斥锁(也称互斥量)可以用来保护关键代码段,以确保其独占式的访问,这有点像二进制信号量.POSIX互斥锁相关函数主要有以下5个: #include <p ...
- Linux多线程编程(不限Linux)
--本文一个例子展开,介绍Linux下面线程的操作.多线程的同步和互斥. 前言 线程?为什么有了进程还需要线程呢,他们有什么区别?使用线程有什么优势呢?还有多线程编程的一些细节问题,如线程之间怎样同步 ...
- ZT 为什么pthread_cond_t要和pthread_mutex_t同时使用 || pthread/Linux多线程编程
为什么线程同步的时候pthread_cond_t要和pthread_mutex_t同时使用 (2009-10-27 11:07:23) 转载▼ 标签: 杂谈 分类: 计算机 举一个例子(http:// ...
- linux查询某域线程是否满了,Linux多线程编程的时候怎么查看一个进程中的某个线程是否存活...
pthread_kill: 别被名字吓到,pthread_kill可不是kill,而是向线程发送signal.还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要用sig ...
- linux线程 ppt,Linux多线程编程多核编程.ppt
<Linux多线程编程多核编程.ppt>由会员分享,可在线阅读,更多相关<Linux多线程编程多核编程.ppt(28页珍藏版)>请在装配图网上搜索. 1.Linux多线程编程, ...
- 多核程序设计 linux,多核程序设计Linux多线程编程.ppt
Linux多线程编程,IEEE POSIX 标准 p1003.1c (Pthreads) 定义了处理线程的一系列C 语言类型的API. 在Linux中,线程一般被认为是"轻量级的进程&quo ...
- Linux 多线程编程(实现生产者消费者模型)
Linux 多线程编程 线程分类 线程按照其调度者可以分为用户级线程和内核级线程两种. 内核级线程 在一个系统上实现线程模型的方式有好几种,因内核和用户空间提供的支持而有一定程度的级别差异.最简单的模 ...
最新文章
- poj 3660(Floyd求传递闭包)
- 使用MyEclipse的注解提示功能以及快捷键总结
- Linux图片的灰度化,iOS图像灰度解决方案--架构设计
- navicat10.1.7英文版_【纯干货】风险评估和管理(PDA TR 49内容节选11 中英文版)...
- python自动登录教程_Python 实现自动登录+点击+滑动验证功能
- JavaScript学习总结(12)——2016 年 7 个顶级 JavaScript 框架
- 九章基础算法02:栈和队列
- Spring之JDBC模板jdbcTemplate
- python找到二维数据矩阵中的最大最小值直接使用min、max函数
- baacloud苹果_Baacloud手机客户端下载
- 硬盘整数分区计算方法一般算法
- 统计推断——假设检验中 p 值的含义具体是什么?
- matlab subs的用法,Matlab subs函數的用法
- HTTP网络劫持的原理与过程、网站被劫持怎么办?
- Angular4中使用PrimeNG calendar
- 北大软件“软件成分分析与漏洞检测工具”(CoBOT—SCA)正式发布
- 加速度中的mg/LSB是什么意思
- BlockingQueue应用
- Zephyr UART
- qq2005beta1/2/3 cs 协议 release 1
热门文章
- java PKCS7Padding 加密Cannot find any provider supporting AES/CBC/PKCS7Padding 解决办法
- Mongodb Replica Configure
- Hello Views之Spinner(yaozq翻译,仅供参考)
- 11月中30个精心设计的网站案例精选
- 数据科学还是计算机科学_您应该拥有数据科学博客的3个原因
- bigquery数据类型_将BigQuery与TB数据一起使用后的成本和性能课程
- 个人项目api接口_5个免费有趣的API,可用于学习个人项目等
- leetcode 377. 组合总和 Ⅳ(dp)
- leetcode915. 分割数组
- javascript 符号_理解JavaScript中“ =”符号的直观指南