等待事件或条件
有时候我们想要让线程运行到某处停下,等待另一个线程通知后在继续运行。代码中创建了一个condition_variable,线程t2运行到17行处调用wait进行等待,wait(std::unique_lock, Predicate)需要传入一个或两个参数,第一个为std::unique_lock(为什么不能是std::lock_guard?),第二个为条件判断。wait()在内部调用传入的lambda函数,判断条件是否成立:若成立(lambda函数返回true),则wait()返回;否则(lambda函数返回false或者未传入),wait()解锁互斥,并令线程进入阻塞状态或等待状态。线程1将数据准备好后,即调用notify_one()或者notify_all()通知条件变量,线程2随之从休眠中觉醒(阻塞解除),重新在互斥上获取锁,再次查验条件:若条件成立,则从wait()函数返回,而互斥仍被锁住;若条件不成立,则线程甲解锁互斥,并继续等待。(防止虚假唤醒的情况)。
我们舍弃std::lock_guard而采用std::unique_lock,原因就在这里:线程2在等待期间,必须解锁互斥,而结束等待之后,必须重新加锁,但std::lock_guard无法提供这种灵活性。 假设线程2在休眠的时候,互斥依然被锁住,那么即使线程1备妥了数据,也不能锁住互斥。结果线程2所等待的条件永远不能成立,它将无止境地等下去。17行[]{return true;}在实际业务中需要替换为实际业务的判断条件。
#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>
int main() {
std::mutex mutex1;
std::condition_variable conditionVariable;
std::thread t1([&]{
std::lock_guard<std::mutex> guard(mutex1);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
conditionVariable.notify_all();
std::cout << "t1 finished \n" << std::endl;
});
std::thread t2([&]{
std::unique_lock<std::mutex> uniqueLock(mutex1);
conditionVariable.wait(uniqueLock, []{return true;});
std::cout << "t2 finished" << std::endl;
});
t1.join();
t2.join();
}
从线程获取返回值
我们可以让线程1把结果写入到一个共享的变量中,然后线程2在一个while循环中检查值是否就绪,或者通过一个定时程序定时检查返回值是否就绪,毫无疑问这样做效率很低,它一直在浪费CPU的时间片。我们可以通过上面通知的方法去取,更直接的,我们可以让线程返回一个值std::future<>,我们直接调用get方法就可以从某个线程中获取返回值,如果值尚未返回,则当前线程会阻塞等待。
#include <thread>
#include <iostream>
#include <future>
int func() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 1;
}
int main() {
std::future<int> future = std::async(func);
std::cout << "do other stuff" << std::endl;
std::cout << future.get();
}
- std::async()方法传入一个可调用对象,返回一个std::future<>,当前线程可以继续做其他事情,然后在future.get()处等待线程返回值。
- td::packaged_task<>连结了future对象与函数(或可调用对象)。std::packaged_task<>对象在执行任务时,会调用关联的函数(或可调用对象),把返回值保存为future的内部数据,std::packaged_task<>的类型是函数签名,传入一个可调用对象,std::packaged_task<>本身也是可调用对象,直接调用就是直接异步线程执行,也可以显式地扔给线程执行。
#include <iostream>
#include <future>
int add(int a, int b)
{
return a + b;
}
int main()
{
// 创建一个打包任务
std::packaged_task<int(int,int)> task(add);
// 获得与任务关联的 future 对象
std::future<int> result = task.get_future();
// 调用 packaged_task,将参数传递给 add
task(2, 3);
// 输出结果
std::cout << "Result is " << result.get() << std::endl;
return 0;
}
显示交给线程执行:
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int add(int a, int b)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
return a + b;
}
int main()
{
// 创建一个打包任务
std::packaged_task<int(int,int)> task(add);
// 获得与任务关联的 future 对象
std::future<int> result = task.get_future();
// 启动一个新线程执行任务
std::thread worker(std::move(task), 2, 3);
// 输出结果
std::cout << "Result is " << result.get() << std::endl;
return 0;
}
- 使用Promise,Promise更加灵活,适合多个线程协同工作。std::promise给出了一种异步求值的方法(类型为T),某个std::future对象与结果关联,能延后读出需要求取的值。在promise调用set_value()设置值之前,调用future.get()的线程将会阻塞等待。
#include <iostream>
#include <thread>
#include <future>
int main()
{
// 创建 promise 对象
std::promise<int> valuePromise;
// 获取与 promise 对象关联的 future 对象
std::future<int> valueFuture = valuePromise.get_future();
// 启动一个新线程设置值
std::thread setterThread([&valuePromise]() {
// 模拟耗时计算
std::this_thread::sleep_for(std::chrono::seconds(2));
// 设置值
valuePromise.set_value(42);
});
// 在主线程中等待并获取值
std::cout << "Waiting for the value..." << std::endl;
int value = valueFuture.get();
std::cout << "Received value: " << value << std::endl;
setterThread.join();
return 0;
}