【问题标题】:C++ Thread question - setting a value to indicate the thread has finishedC++ 线程问题 - 设置一个值以指示线程已完成
【发布时间】:2010-09-07 05:49:38
【问题描述】:

以下安全吗?

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

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

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

【问题讨论】:

    标签: c++ multithreading boost-thread


    【解决方案1】:

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

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

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

    【讨论】:

    • 我不会像处理器上的实际缓存(T1、T2 和 T3)那样担心编译器缓存的值。原因是在多进程机器中,每个处理器的缓存可能不匹配。这就是您使用 volatile 关键字的原因之一。
    • 是的,也许,但请:跟我重复一遍:“volatile bool”不能代替正确的同步。
    【解决方案2】:

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

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

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

    查看thread 文档,您还可以在主线程中调用thread.timed_jointimed_join 将等待指定数量的线程“加入”(加入意味着线程已经结束)

    【讨论】:

      【解决方案3】:

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

      Herb Sutter's Effective Concurrency 系列对 C++ 世界如何与多核/多处理器世界进行了非常深入的探讨。

      【讨论】:

      • 让我再添加一个great read,它演示了确定不使用正确同步原语的程序的结果是多么棘手。
      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案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();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-09-23
          • 1970-01-01
          • 2020-11-10
          • 2010-10-02
          • 1970-01-01
          相关资源
          最近更新 更多