九(续)、std::async深入理解
本节接第九章《获取线程函数返回值》,主要记录了std::async
函数模板与std::thread
函数模板的差异。
1 、std::async的第一个参数
在第九章中介绍过了参数:std::launch::deferred
【延迟调用】;std::launch::async
【强制创建一个线程】
std::launch::deferred
:表示线程入口函数调用被延迟到std::future
的wait()
或者get()
函数调用才执行。如果wait()
和get()
没有调用,则不会创建线程并执行线程入口函数;如果在主线程中调用wait()
和get()
,实际上也不会创建新线程,而是在主线程中执行的入口函数。std::launch::async
:表示强制这个异步任务在新线程上执行,在调用std::async
函数的时候就开始创建线程。std::async
默认的是这个标记。std::launch::deferred | std::launch::async
:这里的“|”意味着async
的行为可能是“创建新的线程并立即执行”或者“没有创建线程并且延迟到调用result.get()才开始执行任务入口函数”。std::async
不带这个标记时,默认是第三种情况,系统自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行。
2、std::thread与std::async的区别
如果系统资源紧张,std::thread()
创建线程可能会失败,那么执行std::thread()
时,整个程序可能崩溃。对于std::async()
我们一般不叫创建线程(尽管async
可以创建线程),我们一般叫它创建一个异步任务。std::async
与std::thread
最明显的不同,就是async
有时候并不创建新的线程。
std::thread
创建线程的方式,如果线程有返回值,不容易拿到这个值。
std::async()
创建异步任务,可能创建也可能不创建线程。并且async
的调用方法很容易拿到线程入口函数的返回值。
由于系统资源的限制:
- 如果使用
std::thread
创建的线程太多,则可能创建失败,系统报告异常,程序崩溃。 - 如果使用
std::async
,一般就不会报异常不崩溃。这是因为系统资源紧张时,async
这种不加额外参数的调用不会创建新线程,而是后续谁调用了result.get()
请求结果,那么这个异步任务就在执行get()
语句的线程中运行。 - 通常来说,一个程序里的线程数量不宜超过100-200。
3、std::async不确定性问题的解决
不加额外参数的std::async
,系统自行决定是否创建新线程。问题的焦点在于std::future<int> result = std::async(mythread);
的写法,这个异步任务到底有没有被推迟执行。我们可以通过result.wait_for()
函数来判断,这在上一讲中也提到过:
1 | std::future<int> result = std::async(mythread); |