例如,我有两个线程,t1
和 t2
。我想等待 t1
或者 t2
完成,这可能吗?
如果我有一系列线程,比如一个 std::vector<std::thread>
,我该怎么做呢?
例如,我有两个线程,t1
和 t2
。我想等待 t1
或者 t2
完成,这可能吗?
如果我有一系列线程,比如一个 std::vector<std::thread>
,我该怎么做呢?
您可以使用std::condition_variable
实现等待和通知(wait & notify),例如:
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;
注意:val
并不一定代表最先完成的线程,因为所有线程都大约在同一时间结束,并且可能会发生覆盖,但这只是为了本示例而言。
.join()
视为不是魔法,而只是互斥锁和条件变量之间通信的语法糖,那么这就是正确的方法。当.join()
的默认行为不是您想要的时候,请自己编写。 - Howard Hinnantthreaded_queue<T>
的实现,将线程的工作产品发送到这样的队列中,并从队列的另一端读取结果。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;
};
#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;
}
Thread slept for 1194ms.
We reacted after a single thread finished!
Thread slept for 1967ms.
Thread slept for 2390ms.
Thread slept for 2984ms.
std::condition_variable
而不是使用while
循环轮询来改进这个问题。 - rwols等待多个线程没有标准的方法。
您需要使用操作系统特定的函数,例如在Windows上使用WaitForMultipleObjects
。
以下是一个仅适用于Windows的示例:
HANDLE handles[] = { t1.native_handle(), t2.native_handle(), };
auto res = WaitForMultipleObjects(2 , handles, FALSE, INFINITE);
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());
非常浪费,仍然不可用,但标准。
waitingThreads.emplace
lambda 中,您是指 thread
而不是 t
吗?(或者在循环中是 t
而不是 thread
?) - Caleth
std::thread
,那么你需要一个外部变量(例如std::atomic_bool
)作为信号。 - Chad