std::async出来的时候,我以为C++终于有了像样的异步编程支持。
用了一段时间后发现不对劲。任务明明提交了,程序却卡住了。调试半天,发现是future析构的时候在等待任务完成。什么异步?这分明是假异步。
后来看了Scott Meyers的《Effective Modern C++》,Item 38专门讲了这个问题,用了一个词:surprising behavior。我觉得太客气了。这就是设计失误。
从C++11的std::async,到C++20的协程,再到C++26即将到来的std::execution,C++异步编程走了15年,终于要走上正轨。这篇文章把这条路完整梳理一遍,让你知道异步编程该怎么写,为什么该那么写。
一、异步编程的本质:我们到底在解决什么问题?
先说清楚一个问题。异步编程到底在解决什么?
很多人会说"提高性能"。没错,但不够准确。更准确的说法是:让CPU不要傻等。
举个例子。你写了一个网络服务器,每来一个请求就要查数据库,查数据库需要10毫秒。这10毫秒CPU在干嘛?在等。数据库返回之前,CPU什么也做不了,就干等着。
如果同时来了1000个请求呢?按同步的写法,你需要1000个线程,每个线程等10毫秒。1000个线程,光线程栈就要消耗好几个GB内存,线程切换的开销也不小。
异步编程的核心思想就是:把等待变成通知。
同步模式下,代码是这样的: