C++并发三剑客:future、promise和async

async用法

std::async是一个用于异步执行函数的模板函数,它返回一个 std::future 对象,该对象用于获取函数的返回值。

#include<iostream>
#include<future>
#include<chrono>

std::string fetchDataFromDB(std::string query) {
	std::this_thread::sleep_for(std::chrono::seconds(5));
	return "Data: " + query;
}

int main() {
	std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "myData");

	//在主线程中做其他的事情
	std::cout << "Doing something else ..." << std::endl;

	//从future对象中获取数据
	std::string dbData = resultFromDB.get();
	std::cout << dbData << std::endl;

	return 0;
}

在这个示例中,std::async 创建了一个新的线程(或从内部线程池中挑选一个线程)并自动与一个 std::promise 对象相关联。std::promise 对象被传递给 fetchDataFromDB 函数,函数的返回值被存储在 std::future对象中。在主线程中,我们可以使用 std::future::get 方法从 std::future 对象中获取数据。注意,在使用 std::async 的情况下,我们必须使用 std::launch::async 标志来明确表明我们希望函数异步执行。

async的启动策略

std::async函数可以接受几个不同的启动策略,这些策略在std::launch枚举中定义。除了std::launch::async之外,还有以下启动策略:

std::launch::deferred:这种策略意味着任务将在调用std::future::get()或std::future::wait()函数时延迟执行。换句话说,任务将在需要结果时同步执行。
std::launch::async | std::launch::deferred:这种策略是上面两个策略的组合。任务可以在一个单独的线程上异步执行,也可以延迟执行,具体取决于实现。

future的wait和get

std::future::get()std::future::wait() 是 C++ 中用于处理异步任务的两个方法,它们的功能和用法有一些重要的区别。
std::future::get():
std::future::get() 是一个阻塞调用,用于获取 std::future 对象表示的值或异常。如果异步任务还没有完成,get() 会阻塞当前线程,直到任务完成。如果任务已经完成,get() 会立即返回任务的结果。重要的是,get() 只能调用一次,因为它会移动或消耗掉 std::future 对象的状态。一旦 get() 被调用,std::future 对象就不能再被用来获取结果。

std::future::wait():
std::future::wait() 也是一个阻塞调用,但它与 get() 的主要区别在于 wait() 不会返回任务的结果。它只是等待异步任务完成。如果任务已经完成,wait() 会立即返回。如果任务还没有完成,wait() 会阻塞当前线程,直到任务完成。与 get() 不同,wait() 可以被多次调用,它不会消耗掉 std::future 对象的状态。

总结

  • std::future::get() 用于获取并返回任务的结果,而 std::future::wait() 只是等待任务完成。
  • get() 只能调用一次,而 wait() 可以被多次调用。
  • 如果任务还没有完成,get() 和 wait() 都会阻塞当前线程,但 get() 会一直阻塞直到任务完成并返回结果,而 wait() 只是在等待任务完成。

你可以使用std::future的wait_for()或wait_until()方法来检查异步操作是否已完成。这些方法返回一个表示操作状态的std::future_status值。

if(fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {  
    // 操作已完成  
} else {  
    // 操作尚未完成  
}

将任务和future关联

std::packaged_taskstd::future是C++11中引入的两个类,它们用于处理异步任务的结果。

std::packaged_task是一个可调用目标,它包装了一个任务,该任务可以在另一个线程上运行。它可以捕获任务的返回值或异常,并将其存储在std::future对象中,以便以后使用。

std::future代表一个异步操作的结果。它可以用于从异步任务中获取返回值或异常。

以下是使用std::packaged_taskstd::future的基本步骤:
1、创建一个std::packaged_task对象,该对象包装了要执行的任务。
2、调用std::packaged_task对象的get_future()方法,该方法返回一个与任务关联的std::future对象。
3、在另一个线程上调用std::packaged_task对象的operator(),以执行任务。
4、在需要任务结果的地方,调用与任务关联的std::future对象的get()方法,以获取任务的返回值或异常。

int myTask() {
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout << "my task run 5 s" << std::endl;
	return 42;
}

void use_package() {
	// 创建一个包装了任务的 std::packaged_task 对象  
	std::packaged_task<int()> task(myTask);

	// 获取与任务关联的 std::future 对象  
	std::future<int> result = task.get_future();

	// 在另一个线程上执行任务  
	std::thread t(std::move(task));
	t.detach();

	//等待任务完成获取结果
	int value = result.get();
	std::cout << "The result is: " << value << std::endl;
}

int main() {
	use_package();
}

在上面的示例中,我们创建了一个包装了任务的std::packaged_task对象,并获取了与任务关联的std::future对象。然后,我们在另一个线程上执行任务,并等待任务完成并获取结果。最后,我们输出结果。

我们可以使用 std::function 和 std::package_task 来包装带参数的函数。std::package_task 是一个模板类,它包装了一个可调用对象,并允许我们将其作为异步任务传递。

promise 用法

C++11引入了std::promisestd::future两个类,用于实现异步编程。std::promise用于在某一线程中设置某个值或异常,而std::future则用于在另一线程中获取这个值或异常。

void setValue(std::promise<int> prom) {
	prom.set_value(10);
}

int main() {
	// 创建一个 promise 对象
	std::promise<int> prom;

	//获取与 promise 相关联的 future 对象
	std::future<int> fut = prom.get_future();

	//在新线程中设置 promise 的值
	std::thread t(setValue, std::move(prom));

	// 在主线程中获取 future 的值
	std::cout << "Waiting for the thread to set the value...\n";
	std::cout << "Value set by the thread: " << fut.get() << '\n';
	t.join();
	return 0;
}

在上面的代码中,
首先创建了一个std::promise<int>对象,然后通过调用get_future()方法获取与之相关联的std::future<int>对象。然后,我们在新线程中通过调用set_value()方法设置promise的值,并在主线程中通过调用fut.get()方法获取这个值。注意,在调用fut.get()方法时,如果promise的值还没有被设置,则该方法会阻塞当前线程,直到值被设置为止。

共享类型future

当我们需要多个线程等待同一个执行结果时,需要使用std::shared_future

假设你有一个异步任务,需要多个线程等待其完成,然后这些线程需要访问任务的结果。在这种情况下,你可以使用std::shared_future来共享异步任务的结果。

void myFunction(std::promise<int>&& promise) {
    // 模拟一些工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    promise.set_value(42); // 设置 promise 的值
}
void threadFunction(std::shared_future<int> future) {
    try {
        int result = future.get();
        std::cout << "Result: " << result << std::endl;
    }
    catch (const std::future_error& e) {
        std::cout << "Future error: " << e.what() << std::endl;
    }
}
void use_shared_future() {
    std::promise<int> promise;
    std::shared_future<int> future = promise.get_future();
    std::thread myThread1(myFunction, std::move(promise)); // 将 promise 移动到线程中
    // 使用 share() 方法获取新的 shared_future 对象  
    std::thread myThread2(threadFunction, future);
    std::thread myThread3(threadFunction, future);
    myThread1.join();
    myThread2.join();
    myThread3.join();
}

在这个示例中,我们创建了一个std::promise<int>对象promise和一个与之关联的std::shared_future<int>对象future。然后,我们将promise对象移动到另一个线程myThread1中,该线程将执行myFunction函数,并在完成后设置promise的值。我们还创建了两个线程myThread2和myThread3,它们将等待future对象的结果。如果myThread1成功地设置了promise的值,那么future.get()将返回该值。这些线程可以同时访问和等待future对象的结果,而不会相互干扰。

参考自:恋恋风辰的官方博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值