我是芯片设计师,我们一直在电路中使用互斥锁进行原子存储器访问。我正在学习使用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()函数从拥有它的线程获取锁定?我花了很多时间在网上寻找答案,但徒劳无功。
我真的很感激这种现象的一些解释。
答案 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
时重新获取锁定(或在尝试重新获取锁定时阻止)。