简体   繁体   English

std :: thread:如何等待(join)任何给定的线程完成?

[英]std::thread: How to wait (join) for any of the given threads to complete?

For example, I have two threads, t1 and t2 . 例如,我有两个线程, t1t2 I want to wait for t1 or t2 to finish. 我想等t1 t2完成。 Is this possible? 这可能吗?

If I have a series of threads, say, a std::vector<std::thread> , how can I do it? 如果我有一系列线程,比如说std::vector<std::thread> ,我该怎么办呢?

There's always wait & notify using std::condition_variable , eg : 总是使用std::condition_variable 等待并通知例如

std::mutex m;
std::condition_variable cond;
std::atomic<std::thread::id> val;

auto task = [&] {
    std::this_thread::sleep_for(1s); // Some work

    val = std::this_thread::get_id();
    cond.notify_all();
};

std::thread{task}.detach();
std::thread{task}.detach();
std::thread{task}.detach();

std::unique_lock<std::mutex> lock{m};
cond.wait(lock, [&] { return val != std::thread::id{}; });

std::cout << "Thread " << val << " finished first" << std::endl;

Note: val doesn't necessarily represent the thread that finished first as all threads finish at about the same time and an overwrite migh occur, but it is only for the purposes of this example. 注意: val不一定代表首先完成的线程,因为所有线程几乎同时完成并且发生覆盖migh,但它仅用于此示例的目的。

No, there is no wait for multiple objects equivalent in C++11's threading library. 不,在C ++ 11的线程库中没有等待多个对象等效。

If you want to wait on the first of a set of operations, consider having them feed a thread-safe producer-consumer queue. 如果要等待一组操作中的第一个操作,请考虑让它们提供一个线程安全的生产者 - 消费者队列。

Here is a post I made containing a threaded_queue<T> . 是我发布的包含threaded_queue<T>的帖子。 Have the work product of your threads be delivered to such a queue. 将线程的工作产品交付到这样的队列中。 Have the consumer read off of the other end. 让消费者阅读另一端。

Now someone can wait on (the work product) of multiple threads at once. 现在有人可以一次等待多个线程的(工作产品)。 Or one thread. 或一个线程。 Or a GPU shader. 或GPU着色器。 Or work product being delivered over a RESTful web interface. 或者通过RESTful Web界面交付工作产品。 You don't care. 你不在乎。

The threads themselves should be managed by something like a thread pool or other higher level abstraction on top of std::thread , as std::thread makes a poor client-facing threading abstraction. 线程本身应该由std::thread类的线程池或其他更高级别的抽象来管理,因为std::thread使得面向客户端的线程抽象不佳。

template<class T>
struct threaded_queue {
  using lock = std::unique_lock<std::mutex>;
  void push_back( T t ) {
    {
      lock l(m);
      data.push_back(std::move(t));
    }
    cv.notify_one();
  }
  boost::optional<T> pop_front() {
    lock l(m);
    cv.wait(l, [this]{ return abort || !data.empty(); } );
    if (abort) return {};
    auto r = std::move(data.back());
    data.pop_back();
    return r;
  }
  void terminate() {
    {
      lock l(m);
      abort = true;
      data.clear();
    }
    cv.notify_all();
  }
  ~threaded_queue()
  {
    terminate();
  }
private:
  std::mutex m;
  std::deque<T> data;
  std::condition_variable cv;
  bool abort = false;
};

I'd use std::optional instead of boost::optional in C++17. 我在C ++ 17中使用std::optional而不是boost::optional It can also be replaced with a unique_ptr , or a number of other constructs. 它也可以用unique_ptr或许多其他结构替换。

It's easy to do with a polling wait: 轮询等待很容易:

#include<iostream>
#include<thread>
#include<random>
#include<chrono>
#include<atomic>

void thread_task(std::atomic<bool> & boolean) {
    std::default_random_engine engine{std::random_device{}()};
    std::uniform_int_distribution<int64_t> dist{1000, 3000};
    int64_t wait_time = dist(engine);
    std::this_thread::sleep_for(std::chrono::milliseconds{wait_time});
    std::string line = "Thread slept for " + std::to_string(wait_time) + "ms.\n";
    std::cout << line;
    boolean.store(true);
}

int main() {
    std::vector<std::thread> threads;
    std::atomic<bool> boolean{false};
    for(int i = 0; i < 4; i++) {
        threads.emplace_back([&]{thread_task(boolean);});
    }
    std::string line = "We reacted after a single thread finished!\n";
    while(!boolean) std::this_thread::yield();
    std::cout << line;
    for(std::thread & thread : threads) {
        thread.join();
    }
    return 0;
}

Example output I got on Ideone.com: 我在Ideone.com上获得的示例输出:

Thread slept for 1194ms.
We reacted after a single thread finished!
Thread slept for 1967ms.
Thread slept for 2390ms.
Thread slept for 2984ms.

This probably isn't the best code possible, because polling loops are not necessarily best practice, but it should work as a start. 这可能不是最好的代码,因为轮询循环不一定是最佳实践,但它应该作为一个开始。

There is no standard way of waiting on multiple threads. 没有标准的等待多线程的方法。

You need to resort to operating system specific functions like WaitForMultipleObjects on Windows. 您需要使用Windows上的WaitForMultipleObjects等特定于操作系统的功能。 A Windows only example: 仅限Windows的示例:

HANDLE handles[] = { t1.native_handle(), t2.native_handle(),  };
auto res = WaitForMultipleObjects(2 , handles, FALSE, INFINITE);

Funnily , when std::when_any will be standardized, one can do a standard but wasteful solution: 有趣的是,当std::when_any将被标准化时,人们可以做一个标准但浪费的解决方案:

std::vector<std::thread> waitingThreads;
std::vector<std::future<void>> futures;
for (auto& thread: threads){
    std::promise<void> promise;
    futures.emplace_back(promise.get_future());
    waitingThreads.emplace_back([&thread, promise = std::move(promise)]{
         thread.join();
         promise.set_value();
    });
}

auto oneFinished = std::when_any(futures.begin(), futures.end());

very wastefull, still not available , but standard. 非常浪费,仍然没有,但标准。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM