请考虑以下示例代码:
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <random>
#include <array>
#include <numeric>
int main()
{
constexpr std::size_t num_mutices = 3;
constexpr std::size_t num_entries = 6;
constexpr std::size_t num_threads = 4;
std::array<std::mutex, num_mutices> mutices;
std::array<std::size_t, num_entries> data;
// fill data with 1, 2, ...
std::iota(data.begin(), data.end(), 1);
// setup random generators
std::random_device rd;
std::vector<std::mt19937> rand_gens;
for(std::size_t i = 0; i < num_threads; i++)
rand_gens.push_back(std::mt19937(rd()));
std::vector<std::thread> threads;
for(std::size_t iThread = 0; iThread < num_threads; iThread++)
{
threads.emplace_back(std::thread([&data, &mutices, &rand_gens, iThread]()
{
// distribution for picking a random value from data
std::uniform_int_distribution<std::size_t> dist(0,data.size()-1);
for(std::size_t a = 0; a < 100; a++)
{
auto iFirst = dist(rand_gens[iThread]);
auto iSecond = dist(rand_gens[iThread]);
std::unique_lock<std::mutex> lock1(mutices[iFirst % mutices.size()]);
std::unique_lock<std::mutex> lock2(mutices[iSecond % mutices.size()]);
std::lock(lock1, lock2);
std::size_t tmp = data[iFirst];
data[iFirst] = data[iSecond];
data[iSecond] = tmp;
mutices[iFirst % mutices.size()].unlock();
mutices[iSecond % mutices.size()].unlock();
}
}));
}
for(std::size_t i = 0; i < num_threads; i++)
threads[i].join();
}
基本上我有多个线程随机访问矢量元素并交换它们。当然,这只是我想要做的最小工作示例(向双向boost :: graph添加边缘)。
问题是这个程序可能会陷入僵局,我不知道如何解释这个问题。
我认为这可以通过使用层次化的突变来完成。我发现了以下很好的解释:Use Lock Hierarchies to Avoid Deadlock
它声明:
规则1:在N级别的互斥锁上持锁时,您可能只在较低级别的互斥锁上获取新锁
因此,我们案例中的水平是所用突变的指数。因此,我应该计算锁定的最小值和最大值以锁定并按顺序获取它们。但是,这仍然会导致死锁,如下所示:
线程1想要锁定互斥锁1和互斥锁3.线程2想要锁定互斥锁2和互斥锁3.现在T1锁定M1,T2锁定M2,现在两者都想获得M3。
我尝试使用try_lock解决这个问题并再次解锁mutice如果线程无法获得所有需要的突变但不知何故我似乎没有得到它:P。