多个线程之间的互斥锁定

时间:2017-03-30 14:58:56

标签: c++ multithreading mutex condition-variable

我是芯片设计师,我们一直在电路中使用互斥锁进行原子存储器访问。我正在学习使用CPP进行编码,我很难理解Mutex在CPP中的工作原理。

我理解任何程序都是一个进程,进程下的线程除了可以在此进程下从任何线程访问的全局对象外,还有自己的本地对象。

在我试图更深入地理解互斥锁时,我在http://www.cplusplus.com/reference/condition_variable/condition_variable/找到了这个例子

1  // condition_variable example
2  #include <iostream>           // std::cout
3  #include <thread>             // std::thread
4  #include <mutex>              // std::mutex, std::unique_lock
5  #include <condition_variable> // std::condition_variable
6  
7  std::mutex mtx;
8  std::condition_variable cv;
9  bool ready = false;
10 
11 void print_id (int id) {
12   std::unique_lock<std::mutex> lck(mtx);
13   while (!ready) cv.wait(lck);
14   // ...
15   std::cout << "thread " << id << '\n';
16 }
17 
18 void go() {
19   std::unique_lock<std::mutex> lck(mtx);
20   ready = true;
21   cv.notify_all();
22 }
23 
24 int main ()
25 {
26   std::thread threads[10];
27   // spawn 10 threads:
28   for (int i=0; i<10; ++i)
29     threads[i] = std::thread(print_id,i);
30 
31   std::cout << "10 threads ready to race...\n";
32   go();                       // go!
33 
34   for (auto& th : threads) th.join();
35 
36   return 0;
37 }

可能的输出(线程顺序可能不同):

10 threads ready to race...
thread 2
thread 0
thread 9
thread 4
thread 6
thread 8
thread 7
thread 5
thread 3
thread 1

在第28和29行中,安排了10个线程。所有这些都试图锁定相同的互斥锁(mtx)。根据我的理解,10个线程中的一个将获得锁定并将在第13行等待,而其他9个线程将尝试获取锁定并在第12行本身被阻止。

然后在第32行调用函数go().Go()也试图在第19行获取相同互斥锁的锁。但它不应该得到它,因为一个print_id()线程拥有锁。根据我的理解,这应该导致死锁,因为go()在第19行被阻止,并且无法越过该行,因此无法发送cv.notify_all();

但链接中的结果另有说法。它表明go()毫不费力地获取了锁,并发送了通知,反过来又启动了对10个线程的多米诺骨牌效应。

我错过了什么? CPP规范中是否嵌入了一些特定规则,允许go()函数从拥有它的线程获取锁定?我花了很多时间在网上寻找答案,但徒劳无功。

我真的很感激这种现象的一些解释。

2 个答案:

答案 0 :(得分:2)

条件变量解锁它们在wait期间内部传递的锁定,并在它们返回之前重新锁定它。

条件变量的一般模式是互斥锁,条件变量和消息的三元组。

侦听器锁定互斥锁,然后在cv.wait(lck)上旋转,直到它检测到消息。它会锁定互斥锁.wait旋转,并且可以处理消息(可能涉及修改它)。

发件人锁定互斥锁,修改邮件,可选择解锁互斥锁,然后执行某种cv.notify(取决于要唤醒的数量)。

虽然cv.wait(lck) lck有时会被解锁,包括暂停等待信号的时间。

所以实际发生的是,一堆线程都在条件变量上排列,并且互斥锁已解锁。

主线程然后获取锁定,设置消息(ready=true),然后执行.notify_all()

实际上,主线程可能会设置消息&amp;在其中一个侦听线程设法到达.notify_all()之前cv.wait(lck)。没有问题,因为如果.notify_all()被调用,ready=true之前已经排序,那么监听线程将 .wait作为{{1} }}循环是while(!ready)

答案 1 :(得分:1)

cv.wait(lock)将在条件变量等待时释放锁定,并在调用notify_all时重新获取锁定(或在尝试重新获取锁定时阻止)。