【线程同步与互斥】互斥锁(mutex)
来源:互联网 发布:mac如何打拼音声调 编辑:程序博客网 时间:2024/06/07 10:36
在多线程访问共享数据的时候可能会发生冲突,例如:
/*冲突的例子*/#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>int common_data=0;//公共数据 void increase_data(void* vptr){//让common_data自增10次//两个线程都来执行它,如果不冲突的话common_data最后会等于20int val; for(int i=0;i<10;i++){ val=common_data; sleep(vptr); printf("线程%d:common_data=%d\n",(unsigned int)pthread_self(),common_data); common_data=val+1;}return NULL;}int main(void){pthread_t tid1,tid2;int err;err=pthread_create(&tid1,NULL,(void*)increase_data,(void *)1);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}err=pthread_create(&tid2,NULL,(void*)increase_data,(void *)2);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0;}
我们会观察到:
数据完全乱套了,没有得到预期的20。
和这个类似的还有一个有趣的例子:/*没有加锁但是没有冲突的情况*/#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>int common_data=0;//公共数据 void pthread1(void* arg);//第一个线程 void pthread1(void* arg);//第二个线程 void pthread1(void* arg){//线程1要修改公共的数据common_data//按照预期,执行后common_data=10 for(int i=0;i<10;i++){common_data++;//sleep(2);printf("pthread1:第%d次循环,common_data=%d\n",i,common_data);}}void pthread2(void* arg){//线程2要修改公共的数据common_data//按照预期,执行后common_data=10//如果两个线程不发生冲突,common_data最后等于20 for(int i=0;i<10;i++){common_data++;//sleep(1);printf("pthread2:第%d次循环,common_data=%d\n",i,common_data);}}int main(void){pthread_t tid1,tid2;int err;err=pthread_create(&tid1,NULL,(void*)pthread1,NULL);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}err=pthread_create(&tid2,NULL,(void*)pthread2,NULL);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0;}
这个例子运行以后没有冲突,这是为什么呢?!在和同学讨论并经过自己动手实践以后证实,其实只要把循环次数增加,比如增加到10000000,还是会观察到冲突的,如下图:
也就是说,当数据量小的时候,执行速度较快,给人的错觉是“顺序”执行的,但是数据量一旦大起来,总有某次读写操作相互过不去......。然后common_data的值就乱了。这也体现了所谓“量变引起质变”吧。
为了解决多个线程访问同一个数据(可能)会出现的冲突问题,一个行之有效的方法就是加互斥锁(MUTual EXclusive lock,mutex)。相关的函数:
(1)pthread_mutex_init
用来申请一个互斥锁。
例如:
pthread_mutex_t mutex;pthread_mutex_init(&mutex,NULL);第一个参数传入pthread_mutex_t类型的指针,第二个指定了需要的属性,一般用NULL。
(2)pthread_mutex_lock
给代码段加锁。加锁意味着当前只有这个锁定这个代码段的线程能够执行它,如果其他线程要执行这些代码,这些线程就会被挂起。
(3)pthread_mutex_trylock
功能同(2),但是这个函数是“尝试加锁”,也就是说如果加锁失败了,函数会立刻返回,线程继续执行。
(4)pthread_mutex_unlock
解锁(但锁还在,区别下面的pthread_mutex_destroy),和(3)、(4)配合使用。
(5)pthread_mutex_destroy
彻底销毁锁,功能同解锁,如果我们不再需要某个锁了就可以销毁它。
有了互斥锁的机制,就可以解决刚才的冲突问题了:
/*没有加锁但是没有冲突的情况*/#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>int common_data=0;//公共数据 pthread_mutex_t mutex; void pthread1(void* arg);//第一个线程 void pthread1(void* arg);//第二个线程 void pthread1(void* arg){//线程1要修改公共的数据common_data//按照预期,执行后common_data=10 for(int i=0;i<10;i++){pthread_mutex_lock(&mutex);//加锁 common_data++;printf("pthread1:第%d次循环,common_data=%d\n",i,common_data);pthread_mutex_unlock(&mutex);//解锁 }}void pthread2(void* arg){//线程2要修改公共的数据common_data//按照预期,执行后common_data=10//如果两个线程不发生冲突,common_data最后等于20 for(int i=0;i<10;i++){pthread_mutex_lock(&mutex);//加锁 common_data++;printf("pthread2:第%d次循环,common_data=%d\n",i,common_data);pthread_mutex_unlock(&mutex);//解锁 }}int main(void){pthread_t tid1,tid2;int err;pthread_mutex_init(&mutex,NULL);//初始化锁 err=pthread_create(&tid1,NULL,(void*)pthread1,NULL);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}err=pthread_create(&tid2,NULL,(void*)pthread2,NULL);if(err!=0){//创建失败 printf("failed to create thread\n");exit(1);}pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mutex);//将锁彻底销毁 return 0;}运行效果:
这一次尽管执行次数很大,但是没有冲突了,得到了预期的2000000。
阅读全文
0 0
- 【线程同步与互斥】互斥锁(mutex)
- 线程之间的同步与互斥mutex
- linux线程互斥与同步(part1)—互斥锁(mutex)的原理及其实现机制
- 线程互斥与同步(part2)—互斥锁(Mutex)的“cp”:条件变量(Condition Variable)
- 线程同步互斥之互斥量(Mutex)
- 线程同步互斥之互斥量(Mutex)
- MFC线程(三):线程同步事件(event)与互斥(mutex)
- C#线程同步系列(四) 互斥对象Mutex
- VC线程同步(互斥对象Mutex)及资源共享....
- linux线程互斥与同步---互斥锁
- linux线程互斥与同步---互斥锁
- 线程同步与互斥:互斥锁
- 线程同步与互斥:互斥锁
- 线程的同步与互斥:互斥锁
- 线程同步与互斥:互斥锁
- 线程同步与互斥(一)
- 使用互斥对象(Mutex)实现不同进程间线程同步
- 用C++和Windows的互斥对象(Mutex)来实现线程同步锁
- 计蒜客之X的平方根
- Java Web 4.2 JDBC访问数据库
- Socket安全
- Apollo阿波罗配置中心
- C++笔记——const关键字
- 【线程同步与互斥】互斥锁(mutex)
- OpenCV编程中调用CUDA时,CMakeLists的编写
- MIPI CSI和DSI接口标准简介
- cf 873 Balanced Substring
- 转自 牛客 1040. 有几个PAT(25)
- 比较三个字符串的大小,最后按从小到大的顺序输出
- LeetCode.16 3Sum Closest
- 【Leetcode-Medium-494】Target Sum
- MIPI DSI协议介绍