线程:如何等待(连接)任何给定的线程完成?

时间:2023-02-03 21:02:50

For example, I have two threads, t1 and t2. I want to wait for t1 or t2 to finish. Is this possible?

例如,我有两个线程,t1和t2。我想等t1或t2结束。这是可能的吗?

If I have a series of threads, say, a std::vector<std::thread>, how can I do it?

如果我有一系列线程,比如std::vector <::thread> ,我怎么做呢?

4 个解决方案

#1


3  

There's always wait & notify using std::condition_variable, e.g.:

总是有等待和通知使用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,但是它只是为了这个示例的目的。

#2


2  

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>. Have the work product of your threads be delivered to such a queue. Have the consumer read off of the other end.

下面是我制作的一个包含threaded_queue 的帖子。将线程的工作产品交付给这样的队列。让消费者读出另一端的信息。

Now someone can wait on (the work product) of multiple threads at once. Or one thread. Or a GPU shader. Or work product being delivered over a RESTful web interface. You don't care.

现在有人可以同时等待多个线程的工作产品。或一个线程。或GPU材质。或者通过RESTful web接口交付的工作产品。你不在乎。

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. It can also be replaced with a unique_ptr, or a number of other constructs.

我将使用std::optional而不是boost:::optional在c++ 17中。也可以用unique_ptr或其他一些结构来替换它。

#3


1  

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.

这可能不是最好的代码,因为轮询循环不一定是最佳实践,但它应该作为一个开始。

#4


0  

There is no standard way of waiting on multiple threads.

不存在等待多个线程的标准方法。

You need to resort to operating system specific functions like WaitForMultipleObjects on Windows. A Windows only example:

您需要使用操作系统特定的功能,比如Windows上的waitformula pleobject。一个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::当任何东西都被标准化时,我们就可以做一个标准但又浪费的解决方案:

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.

非常浪费,仍然没有,但是标准。

#1


3  

There's always wait & notify using std::condition_variable, e.g.:

总是有等待和通知使用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,但是它只是为了这个示例的目的。

#2


2  

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>. Have the work product of your threads be delivered to such a queue. Have the consumer read off of the other end.

下面是我制作的一个包含threaded_queue 的帖子。将线程的工作产品交付给这样的队列。让消费者读出另一端的信息。

Now someone can wait on (the work product) of multiple threads at once. Or one thread. Or a GPU shader. Or work product being delivered over a RESTful web interface. You don't care.

现在有人可以同时等待多个线程的工作产品。或一个线程。或GPU材质。或者通过RESTful web接口交付的工作产品。你不在乎。

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. It can also be replaced with a unique_ptr, or a number of other constructs.

我将使用std::optional而不是boost:::optional在c++ 17中。也可以用unique_ptr或其他一些结构来替换它。

#3


1  

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.

这可能不是最好的代码,因为轮询循环不一定是最佳实践,但它应该作为一个开始。

#4


0  

There is no standard way of waiting on multiple threads.

不存在等待多个线程的标准方法。

You need to resort to operating system specific functions like WaitForMultipleObjects on Windows. A Windows only example:

您需要使用操作系统特定的功能,比如Windows上的waitformula pleobject。一个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::当任何东西都被标准化时,我们就可以做一个标准但又浪费的解决方案:

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.

非常浪费,仍然没有,但是标准。