1.promise和future
我们定义两个函数,分别为mythread,和mythread2:
先看mythread就行,第一个参数是primise用来储存返回值,第二个参数是整型值
再看主函数,启动了mythread,注意第二个参数promise对象不允许复制,必须是指针或者引用(引用传递不会尝试复制对象,只是传递对象的引用(或地址),因此引用传递不会触发复制操作),而且我们也希望把结果储存到这个对象本身而不是复制的副本中。
然后我们通过get_future获得返回值,要注意的future对象也不能被复制,而且也不能用引用传值(如果 std::future
作为引用传递,可能会引发设计上的问题和悬空引用,因为 future
对象是用来获取异步操作的结果的。它的设计是为了确保每个结果只能被获取一次,它可能在传递后可能已经被移动或变得无效),一般通过move(移动语义)来传递。它只能get一次,因为它会移动或消耗掉 future
对象,我们在mythread2中get了,所以main函数的get必须注释掉,否则就出现问题。
调用get_future时,不会阻塞等待,当调用wait或者get的时候,才会阻塞等待,直到设置了结果。
2.packaged_task的使用
packaged_task
是 C++11 引入的一种工具,用于将一个可调用对象(如函数、lambda 表达式、函数对象等)封装在一个任务中,并与 std::future
关联。它允许异步执行任务并在任务完成时获取结果或异常。
packaged_task的间接调用
#include <chrono>
#include <future>
#include <iostream>
#include <thread>
using namespace std;
int mythread(int mypar) {
cout << mypar << endl;
cout << "mythread() Start!"
<< "threadid=" << this_thread::get_id() << endl;
const chrono::milliseconds dura(2000);
this_thread::sleep_for(dura);
return 5;
}
int main() {
cout << "main threadid=" << this_thread::get_id() << endl;
packaged_task<int(int)> mypt(mythread);
future<int> result = mypt.get_future(); // 获取future对象
// 使用std::move传递任务对象
thread t1(move(mypt), 1); // 1代表线程函数的参数
t1.join(); // 等待线程完成
// 线程完成后再获取结果
cout << result.get() << endl; // 只调用一次get
return 0;
}
packaged_task的直接调用
直接调用,整个任务都在主线程执行,没有创建新线程。
3.可多次get的shared_future
其实就是调用future::share方法,shared_future允许多次get
int mythread(int mypar) {
cout << mypar << endl;
cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
chrono::milliseconds dura(2000); // 创建一个持续2秒的时间间隔
this_thread::sleep_for(dura); // 让线程休眠2秒
return 5; // 返回结果5
}
// 函数mythread2接受一个shared_future<int>对象
// 这个函数从shared_future中获取结果并打印
void mythread2(shared_future<int> tmpf) {
auto result = tmpf.get(); // 调用get()获取结果,shared_future允许多次调用get()
cout << "mythread2 result: " << result << endl;
}
int main() {
cout << "main threadid=" << this_thread::get_id() << endl;
// 创建一个packaged_task对象,任务为mythread函数
packaged_task<int(int)> mypt(mythread);
// 获取与packaged_task关联的future对象
future<int> fu1 = mypt.get_future();
// 使用std::move将mypt传递给线程t1
// 因为packaged_task不能被复制,只能被移动
thread t1(move(mypt), 1); // 启动线程t1,传递参数1
t1.join(); // 等待线程t1完成
// 创建shared_future对象,它可以被多个线程共享
shared_future<int> fu2 = fu1.share();
// 检查fu1的有效性
// 在share()之后,fu1将变得无效
bool iscanget = fu1.valid();
cout << "有效or无效?" << iscanget << endl;
// 第一次调用fu2.get(),会阻塞直到任务完成并返回结果
auto m1 = fu2.get();
// 第二次调用fu2.get(),shared_future允许多次获取结果
auto m2 = fu2.get();
cout << "结果为:" << m1 << " " << m2 << endl;
// 启动线程t2,传递shared_future对象fu2
// 这个线程会调用mythread2函数
thread t2(mythread2, fu2);
t2.join(); // 等待线程t2完成
return 0;
}
4.async
(1)基本语法:
std::future<return_type> result = std::async(std::launch::policy, function, args...);
std::launch::policy
:指定异步操作的启动策略。可以是std::launch::async
(强制异步执行)或std::launch::deferred
(延迟执行,直到调用get()
或wait()
)。function
:要异步执行的函数或函数对象。args...
:传递给函数的参数。
(2)基本特性:
-
启动策略:
std::launch::async
:强制异步执行任务,通常会在新线程中执行。std::launch::deferred
:延迟执行任务,直到调用get()
或wait()
。在调用get()
时,如果任务还没有开始,deferred
会在当前线程中同步执行任务。- 可以不指定启动策略,默认为两者的组合,具体行为取决于实现。
-
异常处理:
- 如果异步任务抛出异常,可以通过
get()
访问该异常,get()
会重新抛出任务中的异常。
- 如果异步任务抛出异常,可以通过
-
结果获取:
- 使用
get()
方法获取任务的结果。get()
会阻塞直到任务完成并返回结果。 - 使用
wait()
方法可以等待任务完成,但不获取结果。
- 使用
-
自动管理:
std::async
会自动处理任务的生命周期和线程的创建/销毁,无需显式管理线程的启动和结束。
-
避免资源泄漏:
- 使用
std::future
对象可以避免手动管理线程资源,有效减少资源泄漏的风险。
- 使用
(3)示例代码1
在这个示例中,我们启动了三个并行的异步任务,每个任务计算一个范围的整数之和,并使用 get()
方法获取结果并打印。
int calculateSum(int start, int end)
{
return accumulate(start, end, 0);
}
int main()
{
vector<future<int>> futures;
// 启动多个异步任务
futures.push_back(async(launch::async, calculateSum, 1, 100));
futures.push_back(async(launch::async, calculateSum, 100, 200));
futures.push_back(async(launch::async, calculateSum, 200, 300));
// 获取结果并输出
for (auto& f : futures)
{
cout << "Sum: " << f.get() << endl;
}
return 0;
}
(4)示例代码2
定义一个简单的线程函数
int mythread() {
cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
chrono::milliseconds dura(3000); // 创建一个3000毫秒(3秒)的时间间隔
this_thread::sleep_for(dura); // 让当前线程休眠3秒
return 5; // 返回结果5
}
int main() {
cout << "main threadid=" << this_thread::get_id() << endl;
// 使用 std::async 启动异步任务,使用 std::launch::deferred 启动策略
future<int> result = async(launch::deferred, mythread);
// 由于使用了 launch::deferred,mythread() 不会立即执行,而是延迟到调用 get()
// 或 wait() 时才执行
// 调用 result.get(),这会阻塞直到 mythread() 执行完并返回结果
int def = result.get(); // 获取任务的返回值,mythread() 会在这里执行
cout << def << endl; // 打印返回值
// 也可以调用 result.wait() 来等待任务完成,但不会获取结果
// result.wait(); // 阻塞直到任务完成,但不会返回结果
return 0;
}
(5)示例代码3
// 定义一个简单的线程函数
int mythread() {
cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
chrono::milliseconds dura(5000); // 创建一个5000毫秒(5秒)的时间间隔
this_thread::sleep_for(dura); // 让当前线程休眠5秒
return 5; // 返回结果5
}
int main() {
cout << "main threadid=" << this_thread::get_id() << endl;
// 使用 std::async 启动异步任务,使用默认策略
future<int> result = async(mythread);
// 等待最多4秒检查异步任务的状态
future_status status = result.wait_for(chrono::seconds(4));
if (status == future_status::timeout) {
cout << "线程还没执行完!" << endl;
// 在这里等待异步任务完成,并获取结果
cout << result.get();
} else if (status == future_status::ready) {
cout << "线程成功执行完毕!" << endl;
// 异步任务已经完成,可以获取结果
cout << result.get();
} else if (status == future_status::deferred) {
cout << "线程被延迟执行!" << endl;
// 线程被延迟执行,调用 get() 会立即执行任务
cout << result.get() << endl;
}
return 0;
}