C/C++多线程中互斥锁如何影响内存的可见性

在多线程并发编程中,互斥锁经常被用到,它能防止多线程同时访问临界区,并能保证临界区内存的可见性。本文将会学习C/C++的互斥锁(glibc的实现)是如何影响内存的可见性。

伪代码示例

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| Global Variables : int a = 0, b = 0;                                                         |  
|                    std::mutex mtx;                                                           |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| Thread 1                                   |  Thread 2                                       |   
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| step 1: a = 10; // global variable         |                                                 |   
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| step 2: mtx.lock(); // lock mutex          |                                                 |   
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| step 3: b = 11; // global variable         |                                                 |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
| step 4: mtx.unlock(); // unlock mutex      |                                                 |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
|                                            |  step 5: mtx.lock(); // lock mutex              |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
|                                            |  step 6: print a; // read a, a = 10             |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
|                                            |  step 7: print b; // read b, b = 11             |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
|                                            |  step 8: mtx.unlock(); // unlock mutex          |  
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

代码分析

为了方便理解互斥锁如何影响的内存可见性,以上伪代码被设计的具有时序性,假设线程1和线程2对于互斥锁和全局变量的访问是按照以下顺序进行的。
step 1: 线程1将全局变量a赋值为10;
step 2: 线程1加互斥锁成功;
step 3: 线程1将全局变量b赋值为11;
step 4: 线程1释放互斥锁;
step 5: 线程2加互斥锁成功;
step 6: 线程2打印a的值;
step 7: 线程2打印b的值;
step 8: 线程2释放互斥锁;
注:(1)step 1-4过程中,线程2在做自己的事情,但不会同时修改a和b的值;step 5-8过程中,线程1在做自己的事情,也不会同时修改a和b的值;
(2)step1只是为了展示互斥锁对变量a可见性的影响,实际编程中不提倡在锁外面更改共享变量的值。

执行结果

  1. 线程2在step 6看到了a的值为10,这个值是线程1在step 1赋值的;
  2. 线程2在step 7看到了b的值为11,这个值是线程1在step 3赋值的;

原理分析

线程2之所以能顺利地看到线程1修改的a、b的值,要归功于互斥锁的使用。简单概括一下原理:
(1)互斥锁使用原子操作来保证同一时刻只有一个线程能够进入临界区;
(2) lock操作中包含了一个读内存屏障,线程2的读内存操作(step 6、7)不能跨过step 5;
(3)unlock操作中包含了一个写内存屏障,线程1的写内存操作(step 1和3)不能跨过step 4。

术语happens-before

在谈论内存可见性的问题时,经常会提到happens-before这个术语,那在上述伪代码例子中,就存在以下happens-before场景:
(1)线程1的unlock操作(step 4) happens-before 线程2的lock操作(step 5);
(2)线程1在unlock之前的所有操作happens-before线程2 lock及之后的操作,即Step 1,2,3,4 happens-before step 5,6,7,8。所以线程2在step 6能够看到a=10,在step 7能够看到b=11。

结束语

通过本文的学习,能够理解互斥锁是如何影响内存的可见性,对互斥锁的原理掌握及更深入的无锁编程有很大的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PerfMan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值