【C++】异步编程

什么是异步?

异步编程是一种编程范式,允许程序在等待某些操作完成时继续执行其他任务,而不是阻塞或等待这些操作完成。

  • 在传统的同步编程中,代码是按顺序执行的,每个操作必须等待前一个操作完成。这种方式在处理I/O操作、网络请求或计算密集型任务时可能会导致程序的性能瓶颈。例如,当程序需要从网络上获取数据时,它会等待数据返回后再继续执行,这期间CPU可能会处于空闲状态,浪费了资源。
  • 异步编程则不同,它允许程序在等待操作完成的同时继续执行其他任务。例如,程序可以发起一个网络请求,然后继续执行其他操作,而不必等待网络请求完成。等到网络请求完成时,程序再处理返回的数据。

异步编程的优点

  1. 提高性能:通过并发执行多个任务,异步编程可以更高效地利用CPU资源。
  2. 提高响应速度:异步编程可以使程序在等待某些操作完成时继续响应用户输入,提高用户体验。
  3. 简化I/O操作:异步编程特别适合处理I/O密集型操作,如文件读取,网络请求等。

async和future的使用

什么是async

std::async:是一个函数模板,用于启动一个异步任务。它接受一个可调用的对象(如函数、Lambda表达式、函数对象等)作为参数,并在一个单独的线程上异步执行该对象。std:async自动管理异步任务的生命周期,并返回一个std::future对象,该对象可用于获取异步操作的结果。

什么是future

std::future:是一个模板类,用于表示异步操作的结果。它允许开发者在将来的某个时刻查询异步操作的状
态、等待操作完成或获取操作的结果。通常,std::future对象不是直接创建的,而是与std::async、 std::packaged_task或std::promise配合使用。

future的主要成员函数

在这里插入图片描述

代码示例

#include <iostream>
#include <thread>
#include <future>

using namespace std;

int myfun(int a,int b)
{
	cout << "this is myfun" << endl;
	return a + b;
}

int main()
{
	future<int> myf = async(myfun, 20, 30);
	cout << "myf = " << myf.get() << endl;

	return 0;
}

三种启动策略

future<int> myf = async(launch::async,myfun, 20, 30);
future<int> myf = async(launch::deferred, myfun, 20, 30);
future<int> myf = async(launch::async | launch::deferred, myfun, 20, 30);

第一种:launch::async

  • 行为:该策略指示std::async必须异步地执行任务,即必须在一个新的线程中启动任务。这意味着调用std::async后,任务将立即在新线程中开始执行,而调用线程可以继续执行其他任务。
  • 适用场景:当你需要确保任务在单独的线程中执行,并且不希望它阻塞当前线程时,应使用此策略。

第二种:launch::deferred

  • 行为:该策略指示stdasync可以延迟任务的执行,直到调用std::future::get()或std:future::wait()方法时,任务才在当前线程中执行。如果任务从未被查询(即没有调用get()或wait()),则任务可能永远不会执行。
  • 适用场景:当你希望仅在需要结果时才执行任务,或者当任务执行不会阻塞其他重要操作时,此策略可能很有用。然而,需要注意的是,使用此策略可能会隐藏潜在的并发问题,因为任务可能在查询结果时才执行。

第三种:launch::async | launch::deferred(默认策略)

  • 行为:这个策略是std::launch::async和std::launch::deferred的组合,但它并不保证具体的执行方式。实际执行方式取决于实现(如编译器和库)以及系统资源。在某些情况下,任务可能会异步执行;在其他情况下,它可能会被延迟执行。
  • 适用场景:由于此策略的行为是不确定的,因此它可能不适用于需要精确控制任务执行方式的场景。然而,作为默认策略,它提供了一种灵活的方式来启动异步任务,同时允许实现根据当前系统状况进行优化。

异步编程promise的用法

promise就是类似于一个篮子,用来获取异步的结果。

std::promise是一个用于设置异步操作结果的机制。它允许你在一个线程中设置值或异常,然后在另一个线程中通过std::future对象检索这些值或异常。std::promise通常与std::async、std::packaged_task或 std::thread结合使用,以在异步操作中传递结果。

一个Promise对象是为了保存某个特定的结果值分配的,这个结果值是在未来的某个时间点计算得到的。结果值可以是任意类型的值(void类型除外);也可以是指向常量和非常量(对象指针、函数或者成员函数)的指针;甚至是引用类型的“实值”。
一个Promise对象可以创建一个或多个std::future对象,用来承载该Promise对象所保存的结果值。只有当 set_value()或者set_exception()被调用,这些结果值才能被执行。一旦被执行,这些结果值就可以通过 std::future::get函数添加到std::future对象中。

执行过程

一个线程A生成线程B,在生成线程B的同时生成了一个篮子,把篮子传给了B线程,B拿到篮子之后开始执行结果,把执行结果通过set_value(RS)放进了篮子里,A线程通过future获得放进篮子的结果RS。
在这里插入图片描述

定义promise

你可以通过包含<future>头文件来使用std::promise。然后,你可以创建一个std::promise的实例,并指定它将要持有的值的类型。

	promise<int> prom;

成员函数

在这里插入图片描述

示例代码

#include <iostream>
#include <future>
using namespace std;

void async_task(promise<int> prom)
{
	this_thread::sleep_for(std::chrono::seconds(1));
	prom.set_value(42);//设置异步操作的结果
}

int main()
{
	promise<int> prom; //创建篮子
	future<int> fut = prom.get_future(); //获取与promise 关联的future

	thread t(async_task, move(prom));//启动异步任务,并将promise传递给它
	
	cout << "The result is " << fut.get() << endl;
	t.join();
	return 0;
}

异步编程packaged_task

packaged_task是一个模板类,主要用于将一个可调用对象(如函数、lambda表达式、函数对象等)包装起来,以便异步地执行,并能够获取其返回结果。它和std::future以及std::thread紧密相关,经常用于多线程编程中。

注意:c++17才出现

使用流程

  • 创建packaged_task:
    在创建packaged_task对象时,你需要传递一个可调用对象(如函数、lambda表达式等)给它。这个对象会被封装在 packaged_task中,成为一个异步任务。
  • 获取std::future:
    使用get_future()方法可以获取与packaged_task关联的std::future对象,后者可以用于获取异步任务的结果。
  • 执行任务:
    任务可以通过operator()或者在一个新线程中调用std::thread 来执行。注意,这里的packaged_task对象必须通过std::move传递,因为 packaged_task不允许复制。
  • 获取结果:
    通过调用std::future的get()方法,调用线程可以等待异步任务完成并获取其返回值。如果任务尚未完成,get)将阻塞直到结果可用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值