以下是展示lock_guard
:
#include <iostream>
#include <thread>
#include <mutex>
class OddEven
{
private:
static const int max = 400;
std::mutex mut;
public:
OddEven(){}
OddEven(const OddEven& oddeven)
{
//std::lock_guard<std::mutex> lk(oddeven.mut);
}
void printEven()
{
std::lock_guard<std::mutex> lk(mut);
for(int i=0;i<max;i++)
{
if (i%2 == 0)
std::cout<<i;
else
std::cout<<" ";
}
std::cout<<std::endl;
}
void printOdd()
{
std::lock_guard<std::mutex> lk(mut);
for(int i=0;i<max;i++)
{
//std::cout<<(((i%2)!=0)?i:' ');
if (i%2 != 0)
std::cout<<i;
else
std::cout<<" ";
}
std::cout<<std::endl;
}
};
int main()
{
OddEven oddeven;
std::thread t1(&OddEven::printEven,oddeven);
std::thread t2(&OddEven::printOdd,oddeven);
t1.join();
t2.join();
return 0;
}
预期行为:偶数比奇数或反之亦然
答案 0 :(得分:5)
问题不在于lock_guard
,而在于thread
。 thread
将参数复制/移动到线程私有且可由线程访问的某个位置。由于您提供了左值,因此您的oddeven
对象正在被复制,因此您最终会在两个不同的mutex
es上运行两个不同的对象。
编译器会保护您免受此攻击,因为默认情况下,由于成员(oddEven
)不可复制且不可移动,因此mutex
类是不可复制的且不可移动的。但是通过编写自己的拷贝构造函数,您可以绕过这种保护。不要这样做,并考虑编译器给你的错误。
传递包含在std::ref
中的参数或使用lambda
std::ref
OddEven oddeven;
std::thread t1(&OddEven::printEven, std::ref(oddeven));
std::thread t2(&OddEven::printOdd, std::ref(oddeven));
t1.join();
t2.join();
OddEven oddeven;
std::thread t1([&oddeven] { oddeven.printEven(); });
std::thread t2([&oddeven] { oddeven.printOdd(); });
t1.join();
t2.join();
答案 1 :(得分:4)
看起来您添加了一个复制构造函数来关闭编译器无法复制OddEven
。但是编译器试图帮助你。
当您创建线程时,您将为两个线程提供自己的OddEven
对象,每个对象都有自己的互斥锁。因此,锁定什么都不做。
编译器可能告诉你它在创建线程时无法复制OddEven
,因为它无法生成复制构造函数,因为互斥锁是不可复制的。但是,不是要问为什么在那里复制了对象,而是创建了一个复制构造函数,它为新对象提供了自己的互斥锁。
解决方案是删除复制构造函数并使用oddeven
将std::ref
对象包装在调用中,以便传递对单个对象的引用,而不是每个线程获得自己的对象。
作为旁注,您不应将代码作为外部链接发布,您应该将其内联发布。更容易回答。
答案 2 :(得分:0)
std::thread t1(&OddEven::printEven,oddeven);
std::thread t2(&OddEven::printOdd,oddeven);
电话订单不可预测或偶数或奇数。需要添加同步事件:
printEven(){
...
...
SetEvent(EvenCalculationDoneEvent)
}
printOdd(){...
WaitForSingleObject(EvenCalculationDoneEvent);
....
}
std :: mutex mut - 同步控制台输出,而不是计算顺序 很容易检查这样:
std::thread t1(&OddEven::printEven,oddeven);
Sleep(100);
std::thread t2(&OddEven::printOdd,oddeven);