C ++线程问题-设置值以指示线程已完成

时间:2020-03-05 18:44:54  来源:igfitidea点击:

以下安全吗?

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

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

其中finish_flag是我的类的布尔成员。线程完成后,它会设置该值,然后程序的主循环会检查该值是否有变化。
我认为这是可以的,因为我只启动过一个线程,并且该线程是唯一会更改值的东西(除非在启动线程之前对其进行了初始化)
这样可以吗,还是我丢失了一些东西,需要使用锁和互斥锁等?

解决方案

回答

我们从未提到过finish_flag的类型...

如果这是直言不讳的话,那么它可能会起作用,但是出于多种原因,这无疑是一种不好的做法。首先,一些编译器将缓存Finished_flag变量的读取,因为编译器并不总是能够理解它是由另一个线程写入的。我们可以通过声明布尔变量来解决此问题,但这使我们朝错误的方向前进。即使发生了我们期望的读写操作,也没有什么可以阻止OS调度程序在读/写过程中将两个线程交织在一起。在我们在单独的线程中进行一次读取和一次写入操作的情况下,这可能不是问题,但是从我们要继续进行的角度出发,这是一个好主意。

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

回答

为什么不使用条件来代替使用成员变量来表示线程已完成?我们已经在使用boost库,而condition是线程库的一部分。

一探究竟。它允许工作线程"完成""信号",并且主线程可以在执行过程中检查是否已发出条件信号,然后对完成的工作做任何必要的事情。链接中有示例。

通常,我需要假设资源只能由线程修改。我们可能知道它的用途,但是其他人可能不会引起悲伤,因为主线程认为工作已完成,并试图访问不正确的数据!当工作线程仍在使用它时,它甚至可能将其删除,并导致应用程序崩溃。使用条件可以帮助解决这个问题。

查看thread文档,我们还可以在主线程中调用thread.timed_jointimed_join将等待指定数量的线程" join"(join表示线程已完成)

回答

如果我们确实想了解通过共享内存进行的线程之间通信的详细信息,即使声明变量volatile也是不够的,即使编译器确实使用了适当的访问语义来确保不会获得陈旧的数据版本也是如此。在检查标志之后。 CPU可以发出混乱的读写命令(x86通常不会发出命令,但PPC确实可以发出命令),并且C ++ 9x中没有任何东西允许编译器生成代码来适当地排序内存访问。

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

回答

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

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

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

回答

我并不是要假定,但是似乎finish_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();