代码说明
这是游双的《Linux高性能服务器编程》第 14 章第 5 节中的代码示例,我用 C++11 改写了一下。
主线程试图先占有互斥锁 mutex_a,然后操作被该锁保护的变量 a,但操作完毕之后,主线程并没有立即释放互斥锁 mutex_a,而是又申请互斥 mutex_b,并在两个互斥锁的保护下,操作变量 a 和 b,最后才一起释放这两个互斥锁;与此同时,子线程则按照相反的顺序来申请互斥锁mutex_a, mutex_b,并在两个锁的保护下操作变量 a 和 b。我们用sleep函数来模拟连续两次调用 pthread_mutex_lock 之间的时间差,以确保代码的两个线程各自先占有一个互斥锁(主线程占有 mutex_a,子线程占有 mutex_b),然后等待另外一个互斥锁(主线程等待 mutex_b,子线程等待 mutex_a)。这样,两个线程就僵持住了,谁都不能继续往下执行,从而形成死锁。如果代码中不加入 sleep 函数,则这段代码或许总能成功地行,从而为程序留下了一个潜在的BUG。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream> #include <ostream> #include <thread> #include <mutex> #include <unistd.h>
std::mutex mtx_a; std::mutex mtx_b;
int a = 0; int b = 0;
void thread_work() {
std::cout << "son want mutex b" << std::endl; mtx_b.lock(); b++; std::cout << "son got mutex b, now b is: " << b << std::endl; std::cout << "son want mutex a" << std::endl; mtx_a.lock(); a += b; mtx_a.unlock(); mtx_b.unlock(); }
int main() {
std::cout << "father want mutex a" << std::endl; mtx_a.lock(); a++; std::cout << "father got mutex a, now a is: " << a << std::endl;
std::thread son(thread_work); sleep(1); std::cout << "father want mutex b" << std::endl; mtx_b.lock(); b += a; mtx_b.unlock(); mtx_a.unlock();
son.join();
return 0; }
|
编译运行
因为用到了线程库,编译时需要链接 pthread 库
g++ -o <输出的可执行程序文件名> <源文件名> -lpthread
参考