如何修复C ++线程死锁示例

时间:2014-09-04 08:08:43

标签: c++ multithreading c++11 deadlock

我有一个人为的C ++ 11线程死锁。这是通过使用具有多个线程池的两个单独函数来实现的。如何修复此示例以避免死锁?我认为解决方案与锁定程序的一致排序有关。

#include <thread>
#include <mutex>
#include <iostream>

std::mutex kettle;
std::mutex tap;

#define THREAD_POOL 8

void kettle_tap(){

    std::cout << "Locking kettle in " << std::this_thread::get_id() << std::endl;
    // Lock the mutex kettle by creating and using lock_guard kettle_lock.
    std::lock_guard<std::mutex> kettle_lock(kettle);
    std::cout << "Locked kettle in " << std::this_thread::get_id() << std::endl;

    std::cout << "Locking tap in " << std::this_thread::get_id() << std::endl;
    // Lock the mutex tap by creating and using lock_guard tap_lock.
    std::lock_guard<std::mutex> tap_lock(tap);
    std::cout << "Locked tap in " << std::this_thread::get_id() << std::endl;

    std::cout << "Filling kettle in " << std::this_thread::get_id() << std::endl;
}

void tap_kettle(){

    std::cout << "Locking tap in " << std::this_thread::get_id() << std::endl;
    // Lock the mutex tap by creating and using lock_guard tap_lock.
    std::lock_guard<std::mutex> tap_lock(tap);
    std::cout << "Locked tap in " << std::this_thread::get_id() << std::endl;

    std::cout << "Locking kettle in " << std::this_thread::get_id() << std::endl;
    // Lock the mutex kettle by creating and using lock_guard kettle_lock.
    std::lock_guard<std::mutex> kettle_lock(kettle);
    std::cout << "Locked kettle in " << std::this_thread::get_id() << std::endl;

    std::cout << "Filling kettle in " << std::this_thread::get_id() << std::endl;

}

int main(){

    std::thread pool[THREAD_POOL];

    for (int t = 0; t < THREAD_POOL; t += 2){
        pool[t]   = std::thread(kettle_tap);
        pool[t+1] = std::thread(tap_kettle);
    }

    for (int t = 0; t < THREAD_POOL; ++t){
        pool[t].join();
    }

    std::cout << "Threads are all joined" << std::endl;

    return 0;

}

2 个答案:

答案 0 :(得分:6)

std::lock(Mutexes...)

在您的情况下,kettle_tap()tap_kettle()都应以:

开头
std::lock(tap, kettle);

但互斥参数的顺序无关紧要,因此两个函数的可能不同

  

锁定多个互斥锁

     

锁定作为参数传递的所有对象,并在必要时阻止调用线程。

     

该函数使用对其成员lock,try_lock和unlock的未指定的调用序列来锁定对象,以确保返回时所有参数都被锁定(不会产生任何死锁)。

     

如果函数无法锁定所有对象(例如因为其内部调用之一引发异常),则该函数首先解锁所有成功锁定的对象(如果有),然后再失败。

稍后,如果您想将锁的所有权转移到std::lock_guard

std::lock(tap, kettle);
std::lock_guard<std::mutex> kettle_lock(kettle, std::adopt_lock);
std::lock_guard<std::mutex> tap_lock(tap, std::adopt_lock);

答案 1 :(得分:3)

你是对的。通过避免循环等待,死锁可以是prevented。在您的示例中,为避免死锁,请在kettle_lock方法中将tap_lock移到tap_kettle之上。通过这种方式,您可以获得部分订购。