线程化类成员函数;线程初始化通过初始化列表
问题描述:
我试图从类成员函数创建一个胎面,并通过类构造函数初始化程序列表初始化所述线程。线程化类成员函数;线程初始化通过初始化列表
在调用Receive_List.push_back(CurVal++)
期间执行线程异常时,通过简单地将printf()
作为函数中的第一条指令,可以避免此异常。
#include <thread>
#include <list>
class SomeClass
{
std::thread Receive_Thread;
std::list<unsigned int> Receive_List;
void Receive_Main()
{
//printf("Hacky Way Of Avoiding The Exception\n");
const unsigned int MaxVal = 3000;
unsigned int CurVal = 0;
while (CurVal < MaxVal)
{
Receive_List.push_back(CurVal++);
}
}
public:
SomeClass() :
Receive_Thread(std::thread(&SomeClass::Receive_Main, this))
{}
~SomeClass()
{
Receive_Thread.join();
}
void ProcessReceiveList()
{
if (!Receive_List.empty())
{
printf("Received Val: %i\n", Receive_List.front());
Receive_List.pop_front();
}
}
bool IsReceiveEmpty()
{
return Receive_List.empty();
}
};
int main()
{
SomeClass* MyObject = new SomeClass();
//
// Sleep for 1 second to let the thread start populating the list
std::this_thread::sleep_for(std::chrono::seconds(1));
while (!MyObject->IsReceiveEmpty())
{
MyObject->ProcessReceiveList();
}
delete MyObject;
std::system("PAUSE");
return 0;
}
这是怎么发生的?
答
您正在观察的问题是由列表初始化之前启动的线程引起的,给出数据竞赛,这会导致未定义的行为。添加printf
会延迟第一次访问列表,以便初始化更可能在访问之前完成。这不是而是虽然修正了数据竞争;它可以通过固定前声明列表螺纹:
std::list<unsigned int> Receive_List;
std::thread Receive_Thread;// WARNING: must be initialised last
你有进一步的问题:所有访问至由一个线程修改和更新由另一个必须同步数据;通常通过守卫它mutex。如果没有同步,您又会遇到数据竞争,导致未定义的行为。
所以互斥添加到类守卫名单:
#include <mutex>
class SomeClass {
std::mutex mutex;
//...
};
并锁定它,当你访问列表
while (CurVal < MaxVal)
{
std::lock_guard<std::mutex> lock(mutex);
Receive_List.push_back(CurVal++);
}
,同样在访问列表中的其他功能。
这对我来说没有意义,为什么取消注释'printf()'允许代码无例外地执行。我的印象是,简单地使用list :: push和list :: pop函数可以同时运行,没有问题。另外,将主线程中的while循环替换为'while(true){}'仍然会导致异常。 – KKlouzal 2014-09-03 10:40:57
@KKlouzal:未定义的行为往往没有意义。不,标准容器不同步;你需要自己照顾自己。确实还有另外一个问题,我会在答案中加入。 – 2014-09-03 10:47:07
非常感谢您提供的所有信息。我不知道在类定义中声明变量的顺序表示它们被初始化的顺序。 – KKlouzal 2014-09-03 10:51:38