C++线程问题-设置一个值来指示线程已经完成

问题描述 投票:0回答:5

以下安全吗?

我是线程新手,我想将一个耗时的进程委托给我的 C++ 程序中的一个单独的线程。 使用 boost 库,我编写了如下代码:

thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);

其中

finished_flag
是我班的
bool
成员。当线程完成时,它会设置该值,并且我的程序的主循环会检查该值的更改。 我认为这是可以的,因为我只启动一个线程,并且该线程是唯一改变该值的东西(除了在启动线程之前初始化它时) 那么这可以吗,还是我遗漏了一些东西,需要使用锁和互斥锁等。

c++ multithreading boost-thread
5个回答
11
投票

你从来没有提到finished_flag的类型...

如果它是一个直的bool,那么它可能会起作用,但由于多种原因,这肯定是不好的做法。首先,一些编译器会缓存 finished_flag 变量的读取,因为编译器并不总是能识别出它正被另一个线程写入的事实。您可以通过声明 bool volatile 来解决这个问题,但这将我们引向错误的方向。即使读取和写入如您所期望的那样发生,也没有什么可以阻止操作系统调度程序在读/写过程中交错两个线程。在这里,您在单独的线程中有一个读取操作和一个写入操作,这可能不是一个问题,但按照您的意思继续进行是一个好主意。

另一方面,如果它是线程安全类型,就像 MFC 中的 CEvent (或 boost 中的等效类型)那么你应该没问题。这是最好的方法:使用线程安全的同步对象进行线程间通信,即使对于简单的标志也是如此。


7
投票

为什么不使用

condition
,而不是使用成员变量来表示线程已完成?您已经在使用 boost 库,并且
condition
是线程库的一部分。

检查出来。它允许工作线程“发出信号”已完成,并且主线程可以在执行期间检查条件是否已发出信号,然后对已完成的工作执行所需的任何操作。链接中有示例。

作为一般情况,我永远不会假设资源只能由线程修改。您可能知道它的用途,但其他人可能不知道 - 造成无尽的悲伤,因为主线程认为工作已完成并尝试访问不正确的数据!它甚至可能在工作线程仍在使用它时删除它,并导致应用程序崩溃。使用

condition
会有所帮助。

查看

thread
文档,您还可以在主线程中调用
thread.timed_join
timed_join
将等待指定的时间让线程“加入”(加入意味着线程已结束)


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();

5
投票

如果你真的想通过共享内存了解线程之间通信的细节,即使声明一个变量 volatile 也是不够的,即使编译器确实使用了适当的访问语义来确保它不会获得过时的版本检查标志后的数据。只要 CPU 可以发出乱序读取和写入(x86 通常不会,但 PPC 肯定会),并且 C++9x 中没有任何内容允许编译器生成代码来适当地排序内存访问。

Herb Sutter 的有效并发 系列非常深入地探讨了 C++ 世界如何与多核/多处理器世界相交。


2
投票

让线程在退出之前设置一个标志(或发出一个事件信号)是一种竞争条件。该线程不一定已返回操作系统,并且可能仍在执行。

例如,考虑一个加载动态库(伪代码)的程序:

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()
。这将确保线程已返回操作系统并且不再执行。

© www.soinside.com 2019 - 2024. All rights reserved.