【问题标题】:Can multiple threads join the same boost::thread?多个线程可以加入同一个 boost::thread 吗?
【发布时间】:2016-02-10 05:25:55
【问题描述】:

如果多个线程尝试加入同一个线程,pthreads 的行为未定义:

如果多个线程同时尝试加入同一个线程, 结果未定义。

boost::threads 也一样吗?文档似乎没有指定这一点。

如果未定义,那么多个线程等待一个线程完成的干净方式是什么?

【问题讨论】:

  • 嗯,有趣的是,我可以阅读的 N4296 C++ 标准草案中没有提到它。我会说那是UB和/或“不要这样做”的行为。 :)
  • 其他线程可以测试一个线程是否为joinable(),或者关闭线程可以使用condition_variable来表示它的所有工作已经完成(即使它可能仍然运行)
  • @wilx, std::thread::join() 是一个非常量函数,因此同时调用它是一个数据竞争,即未定义的行为。除非另有说明,否则该规则适用于所有标准库类型。
  • @Tas,如果两个线程同时调用joinable() 是什么阻止它们然后尝试调用join()?即使只有一个调用join(),但没有某种同步,join() 调用与另一个线程中的joinable() 调用冲突,导致数据竞争,即未定义的行为。

标签: c++ multithreading boost


【解决方案1】:

如果未定义,那么多个线程等待一个线程完成的干净方式是什么?

干净的方法是让一个线程通知其他线程它已完成。一个packaged_task 包含一个future 可以等待,在这里可以帮助我们。

这是一种方法。我使用了 std::thread 和 std::packaged_task,但你也可以使用 boost 等价物。

#include <thread>
#include <mutex>
#include <future>
#include <vector>
#include <iostream>

void emit(const char* msg) {
    static std::mutex m;
    std::lock_guard<std::mutex> l(m);
    std::cout << msg << std::endl;
    std::cout.flush();
}

int main()
{
    using namespace std;

    auto one_task = std::packaged_task<void()>([]{
        emit("waiting...");
        std::this_thread::sleep_for(std::chrono::microseconds(500));
        emit("wait over!");
    });

    // note: convert future to a shared_future so we can pass it
    // to two subordinate threads simultaneously
    auto one_done = std::shared_future<void>(one_task.get_future());
    auto one = std::thread(std::move(one_task));

    std::vector<std::thread> many;
    many.emplace_back([one_done] {
        one_done.wait();
        // do my thing here
        emit("starting thread 1");
    });

    many.emplace_back([one_done] {
        one_done.wait();
        // do my thing here
        emit("starting thread 2");
    });

    one.join();
    for (auto& t : many) {
        t.join();
    }

    cout << "Hello, World" << endl;
    return 0;
}

预期输出:

waiting...
wait over!
starting thread 2
starting thread 1
Hello, World

【讨论】:

    【解决方案2】:

    我最终使用了boost::condition_variable... 大致:

    class thread_wrapper {
        boost::mutex mutex;
        boost::condition_variable thread_done_condition;
        bool thread_done = false;
    
        void the_func() {
            // ...
            // end of the thread
            {  
                boost:unique_lock<boost::mutex> lock(mutex);
                thread_done = true;
            }
            thread_done_condition.notify_all();
        }
    
        void wait_until_done() {
            boost::unique_lock<boost::mutex> lock(mutex);
            thread_done_condition.wait(lock, [this]{ return thread_done; });
        }
    }
    

    然后多个呼叫者可以安全地呼叫wait_until_done()

    【讨论】:

      【解决方案3】:

      现在让我震惊的是,类似以下的方法也可以工作:

      class thread_wrapper {
      public:
          thread_wrapper() : thread([this]() { this->the_func(); }) { }
      
          void wait_until_done() {
              boost::unique_lock<boost::mutex> lock(join_mutex);
              thread.join();
          }
      
      private:
          void the_func() {
              // ...
          }
      
          boost::mutex join_mutex;
          boost::thread thread;
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-29
        • 1970-01-01
        • 2013-04-07
        • 2023-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-01
        相关资源
        最近更新 更多