std::async与std::thread

九(续)、std::async深入理解

本节接第九章《获取线程函数返回值》,主要记录了std::async函数模板与std::thread函数模板的差异。

1 、std::async的第一个参数

在第九章中介绍过了参数:std::launch::deferred【延迟调用】;std::launch::async【强制创建一个线程】

  1. std::launch::deferred:表示线程入口函数调用被延迟到std::futurewait()或者get()函数调用才执行。如果wait()get()没有调用,则不会创建线程并执行线程入口函数;如果在主线程中调用wait()get(),实际上也不会创建新线程,而是在主线程中执行的入口函数。
  2. std::launch::async:表示强制这个异步任务在新线程上执行,在调用std::async函数的时候就开始创建线程。std::async默认的是这个标记。
  3. std::launch::deferred | std::launch::async:这里的“|”意味着async的行为可能是“创建新的线程并立即执行”或者“没有创建线程并且延迟到调用result.get()才开始执行任务入口函数”。
  4. std::async不带这个标记时,默认是第三种情况,系统自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行。

2、std::thread与std::async的区别

如果系统资源紧张,std::thread()创建线程可能会失败,那么执行std::thread()时,整个程序可能崩溃。对于std::async()我们一般不叫创建线程(尽管async可以创建线程),我们一般叫它创建一个异步任务std::asyncstd::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
2
3
4
5
6
7
8
9
10
11
12
std::future<int> result = std::async(mythread);          
std::future_status status = result.wait_for(std::chrono::seconds(0s)); //0s的语法没问题,operator ""s
if (status == std::future_status::deferred)
{
// 如果async的第一个参数被设置为std::launch::deferred,则本条成立
cout << "线程被延迟执行" << endl;
cout << result.get() << endl; // 这时候线程入口函数才会执行
}
else
{
cout<<"任务没有被推迟"<<endl;
}
听说打赏我的人,最后都找到了真爱。