我试图理解为什么我的互斥体的行为不像我期望的那样。
我正在调试另一个问题,并决定制作一个非常简单的可执行文件来直接测试互斥行为。这就是我想出的:
#include <mutex>
#include <thread>
#include <iostream>
#include <chrono>
int main(int argc, char** argv)
{
std::mutex myMutex;
auto threadGenerator = [&] (std::string printout)
{
auto threadFunctor = [&, printout] {
int count = 0;
while (count < 300)
{
std::lock_guard<std::mutex> lock(myMutex);
std::cout << printout << std::endl;
count++;
// Sleep ensures that the other thread will be waiting on mutex
// when I release lock
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};
return threadFunctor;
};
auto thread1Functor = threadGenerator("Thread 1 got lock");
auto thread2Functor = threadGenerator("Thread 2 got lock");
std::thread thread1(thread1Functor);
std::thread thread2(thread2Functor);
thread1.join();
thread2.join();
return 0;
}
这只会产生两个线程,它们会反复锁定和解锁互斥锁,同时打印一些输出。我添加了sleep以强制lock_guard阻塞,并且线程彼此等待。
这会产生以下输出:
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
最终,一旦线程1完成,线程2将再次开始获取锁定。
它们不应该在线程1和线程2之间交替吗?应该在每次循环迭代结束时释放锁,这应该允许另一个线程控制互斥锁。为什么不发生这种情况?有什么办法可以实现吗?
答案 0 :(得分:3)
在将互斥锁定在单独的范围内时放置代码:
while (count < 300) {
{
std::lock_guard<std::mutex> lock(myMutex);
std::cout << printout << std::endl;
count++;
} // lock is released here
// ...
}
答案 1 :(得分:2)
它们不应该在线程1和线程2之间交替吗?
没有这样的保证。
应该在每次循环迭代结束时释放锁,这应该允许另一个线程控制互斥锁。为什么不发生这种情况?
因为你的第一个线程锁定互斥锁,睡眠,解锁互斥锁然后再次尝试锁定互斥锁。现在,线程1和线程2都试图获取互斥锁,但是线程1处于运行状态,而线程2正在休眠,因此线程1更有可能首先获取互斥锁。
有没有办法让它成为现实?
您的程序不应该区分线程,不应该依赖于顺序。在实际情况中,多个线程等待mutex获取数据,一个线程将其放在那里,因此所有等待线程都处于等待状态,因此它们获得互斥的概率相似。但这可能是针对特定硬件,操作系统和版本的。你的程序不应该依赖于哪个特定的线程获得了互斥。
答案 2 :(得分:0)
正如@πάνταῥεῖ所说,每次线程进入While循环时,范围都由While的部分分隔。因此,每当while中的代码完成时,std::lock_guard<std::mutex>
都会被销毁,等等,每次线程再次执行while部分时都会创建一个新的作用域。
根据CppReference http://en.cppreference.com/w/cpp/thread/lock_guard:
当控件离开创建lock_guard对象的作用域时,将破坏lock_guard并释放互斥锁。 lock_guard类是不可复制的。
这就是你有这样一个行为的原因:
如果你想锁定所有的threadFunctor,你应该暂时取出lock_guard,如下所示:
std::mutex myMutex;
auto threadGenerator = [&](std::string printout)
{
auto threadFunctor = [&, printout] {
//Scope of threadFunctor
int count = 0;
std::lock_guard<std::mutex> lock(myMutex); //The Lock is working into the Scope of threadFunctor
while (count < 100)
{
//New Scope is create (While's Scope)
std::cout << printout << std::endl;
count++;
// Sleep ensures that the other thread will be waiting on mutex
// when I release lock
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
//At this point the Lock will be released.
};
return threadFunctor;
};