Skip to content

Latest commit

 

History

History
34 lines (22 loc) · 2.32 KB

File metadata and controls

34 lines (22 loc) · 2.32 KB

条款35:优先选用基于任务而非基于线程的程序设计

如果你想以异步方式运行函数 doAsyncWork,有两种基本选择。你可以创建一个 std::thread ,并在其上运行 doAsyncWork,因此这是基于线程的途径:

int doAsyncWork();
std::thread t(doAsyncWork);

你也可以把 doAsyncWork 传递给 std::async ,这种策略叫做基于任务:

auto fut = std::async(doAsyncWork);

在这样的调用中,传递给 std::async 的函数对象,被看作 任务(task)

基于任务的方法通常比基于线程实现的对应版本要好。 std::async 返回的期值提供了 get 函数,如果 doAsyncWork 抛出了异常,get 函数能访问到该异常。而采用了基于线程的途径,无法轻松访问到函数的返回值,并且在 doAsyncWork 抛出异常时,程序会终止。

“线程”在带有并发C++软件中的三种意义:

  • 硬件线程是实际执行计算的线程。
  • 软件线程(系统线程)是操作系统用以实施跨进程的管理,以及进行硬件线程调度的线程。
  • std::thread 是C++进程里的对象,用作底层软件线程的句柄。

如果软件线程数量超过系统能够提供的数量,就会抛出异常。即使没有用尽线程,还是会发生超订问题,线程调度器会为软件线程在硬件线程之上分配CPU时间片,这会为调度器造成高昂的管理开销。软件线程和硬件线程的最佳比例取决于软件线程编程可运行状态的平凡程度,而这是会动态地改变的,需要考虑均衡负载。

std::async 可以处理这些问题,将线程管理的责任交给了C++标准库的实现者。默认情况下 std::async 保证不会创建一个新的软件现场,它允许调度器把指定函数运行在请求 doAsyncWork 结果的线程中,如果系统发生了超定或线程耗尽,合理的调度器就可以利用这个自由度。

但是仍有几种情况下,直接使用线程会更适合:

  • 你需要访问底层线程实现的API。 如C++没有线程优先级和亲和性的概念,系统底层线程库可能提供这些API。
  • 你需要且有能力为你的应用优化线程用法。 例如开发服务器软件。
  • 你需要实现超越C++并发API的线程技术。 如线程池。