10

以下安全吗?

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

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

其中finished_flag 是我班的布尔成员。当线程完成时,它会设置值,我的程序的主循环会检查该值的变化。我认为这没关系,因为我只启动一个线程,并且该线程是唯一改变值的东西(除非在我启动线程之前初始化它)所以这可以吗,或者我错过了什么,并且需要使用锁和互斥锁等

4

5 回答 5

11

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

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

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

于 2008-08-29T08:10:55.983 回答
7

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

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

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

查看thread文档,您还可以调用thread.timed_join主线程。timed_join将等待指定数量的线程“加入”(加入意味着线程已完成)

于 2008-08-29T08:20:22.433 回答
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();
于 2008-09-21T18:47:59.027 回答
5

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

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

于 2008-08-29T23:16:26.683 回答
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为 时truemyclass::mymethod()尚未返回。至少,它仍然必须执行某种“返回”指令(如果不是更多:析构函数、异常处理程序管理等)。如果执行的线程myclass::mymethod()在此之前被抢占,someFunction()将返回调用程序,调用程序将卸载库。当正在执行的线程myclass::mymethod()被安排再次运行时,包含“return”指令的地址不再有效,程序崩溃。

解决方案是在返回之前someFunction()打电话thrd->join()。这将确保线程已返回操作系统并且不再执行。

于 2008-09-16T05:19:54.760 回答