以下是否安全?
我是线程新手,我想将一个耗时的过程委托给我的C ++程序中的一个单独的线程。 使用boost库我编写了类似这样的代码:
thrd = new boost :: thread(boost :: bind(& myclass :: mymethod,this,& finished_flag);
其中finished_flag是我班级的布尔成员。线程完成后,它会设置值,程序的主循环会检查该值的变化。 我认为这是可以的,因为我只启动一个线程,并且该线程是唯一改变值的东西(除非在我启动线程之前初始化它) 所以这没关系,或者我错过了什么,需要使用锁和互斥锁等
答案 0 :(得分:11)
您从未提及过finished_flag的类型......
如果它是一个直的 bool ,那么它可能会起作用,但由于几个原因,这肯定是不好的做法。首先,一些编译器将缓存 finished_flag 变量的读取,因为编译器并不总是认为它是由另一个线程写入的事实。您可以通过声明 bool volatile 来解决这个问题,但这会让我们走错方向。即使读取和写入正如您所期望的那样发生,也没有什么可以阻止OS调度程序在读/写中途交错两个线程。这可能不是一个问题,你在一个单独的线程中有一个读操作和一个写操作,但是最好开始你的意思继续。
另一方面,如果它是一个线程安全类型,如CEvent in MFC(或equivilent in boost)那么你应该没问题。这是最好的方法:使用线程安全的同步对象进行线程间通信,即使对于简单的标志也是如此。
答案 1 :(得分:7)
为什么不使用condition
而不是使用成员变量来表示线程已完成?您已经在使用boost库,condition
是线程库的一部分。
检查out。它允许工作线程“发出信号”已完成,并且主线程可以在执行期间检查条件是否已经发出信号,然后执行它对完成的工作所需的任何操作。链接中有一些例子。
作为一般情况,我会假设资源只会被线程修改。你可能知道它是什么,但是其他人可能不会 - 因为主线程认为工作已经完成并试图访问不正确的数据而导致悲伤的结局!它甚至可能在工作线程仍在使用它时删除它,并导致应用程序崩溃。使用condition
将有助于此。
查看thread
文档,您还可以在主线程中调用thread.timed_join
。 timed_join
将等待线程的指定数量“加入”(连接意味着线程已经finsihed)
答案 2 :(得分:5)
如果你真的想通过共享内存深入了解线程之间的通信细节,即使编译器确实使用适当的访问语义来确保它不会变得陈旧,即使声明变量volatile也是不够的检查标志后的数据版本。 CPU可以长时间不按顺序发出读写(x86通常不会,但PPC肯定会这样做)并且C ++ 9x中没有任何内容允许编译器生成代码以正确地命令内存访问。
Herb Sutter's Effective Concurrency系列深入探讨了C ++世界如何与多核/多处理器世界相交叉。
答案 3 :(得分:5)
我不是故意推测,但似乎 finished_flag 变量的目的是暂停主线程(在某些时候),直到线程 thrd 已经完成了。
最简单的方法是使用 boost :: thread :: join
// launch the thread...
thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);
// ... do other things maybe ...
// wait for the thread to complete
thrd.join();
答案 4 :(得分:2)
让线程在退出之前设置标志(或发出事件信号)是竞争条件。该线程尚未返回到OS,并且可能仍在执行。
例如,考虑一个加载动态库(伪代码)的程序:
lib = loadLibrary("someLibrary");
fun = getFunction("someFunction");
fun();
unloadLibrary(lib);
让我们假设这个库使用你的线程:
void someFunction() {
volatile bool finished_flag = false;
thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);
while(!finished_flag) { // ignore the polling loop, it's besides the point
sleep();
}
delete thrd;
}
void myclass::mymethod() {
// do stuff
finished_flag = true;
}
当myclass::mymethod()
将finished_flag
设置为true
时,myclass::mymethod()
尚未返回。至少,它仍然必须执行某种“返回”指令(如果不是更多:析构函数,异常处理程序管理等)。如果执行myclass::mymethod()
的线程在该点之前被抢占,someFunction()
将返回到调用程序,并且调用程序将卸载该库。当执行myclass::mymethod()
的线程被安排再次运行时,包含“return”指令的地址不再有效,程序崩溃。
解决方案是让someFunction()
在返回之前调用thrd->join()
。这将确保线程已返回到操作系统并且不再执行。