我们日常写的程序大部分的都是基于同步( Synchronous),即按编码顺序一条条执行——这是最符合人类直觉的编码方式,然而当I/O的速度和CPU计算的速度不匹配时,会出现CPU资源浪费,为了解决这种问题,C++提供了异步(Asynchronous)处理的方法。

同步 vs 异步

同步和异步描述的是I/O事件完成后获知结果的方式:

  • 同步的方式是进入函数之后,等做完所有事情之后主动返回结果
  • 异步的方式是进入函数之后立刻返回,被动等待任务完成的消息后再获取结果

关于future 和 get

注意:future本来并不产生结果,只是一个凭证,是通过.get()获从async、以及promise和package_task中取结果,并且get调用时,如果函数本身还没有返回,则会发生阻塞等待

async跑简单异步任务

这是最简单的设置异步方式,通过future<>来表示未来某个将获取的返回结果fut,async来承担实际调用(传入需要目标函数和此函数所需要的参数),async调用所得的结果用get来获取

int heavyWork(int a, int b) {
    sleep(1); //模拟耗时任务
    return a + b;
}

int main() {
    std::future<int> fut = std::async(heavyWork, 3, 4);
	// 并不等待heavyWork真正返回结果,而是可以去做其他的事情

    int res = fut.get();

    std::cout << "res: " << res << std::endl;
    return 0;
}

输出:

res: 7

promise精细控制

刚才讲的async方式是自动化的——设置future、函数调用、结果返回都是被自动设置的。

//一行全部搞定
std::future<int> fut = std::async(heavyWork, 3, 4);

有些时候我们希望更精细地控制异步结果返回的整个过程,就需要使用promise

控制方式

promise 的成员函数:

  • .get_future() 设置future
  • .set_value() 设置结果返回

手动调用heavyWork()`函数

//多传一个参数 prom
void heavyWork(int a, int b, std::promise<int> prom) {
    sleep(1); //模拟耗时任务
    prom.set_value(a + b);    
}

int main() {
    std::promise<int> prom;
    // 1.设置future
    std::future<int> fut = prom.get_future();

    heavyWork(3, 4, std::move(prom));
    int res = fut.get();
    std::cout << "res: " << res << std::endl;

    return 0;
}

其实async的功能其底层实现就是promise

packaged_task打包进线程

packaged_task特别适合打包任务推送进线程池的模式(即生产者-消费者模式),受篇幅所限,这里不展示线程池的实现,下面的例子展示了一线程一执行的方式:

int heavyWork(int a, int b) {
    sleep(1); //模拟耗时任务
    return a + b;
}

int main() {
    std::packaged_task<int(int, int)> task(heavyWork);
    std::future<int> fut = task.get_future();    
    std::thread t(std::move(task), 3, 4);

    t.detach(); //heavyTask任务从主线程剥离

    int res = fut.get();

    std::cout << "res: " << res << std::endl;
}

注意: std::thread里的detach方法将当前任务从主线程剥离,任务结束后自动回收,不需要join手动回收线程

更多推荐