Stroustrup引用的std :: async的限制是什么?

5go*_*der 17 concurrency multithreading asynchronous language-lawyer c++11

在他的"C++编程语言"(第4版)一书的"教程"一章5.3.5.3中,Bjarne Stroustrup撰写了关于该std::async功能的文章.

有一个明显的局限性:甚至不要考虑使用async()共享需要锁定的资源的任务 - async()你甚至不知道thread将使用多少个,因为这async()可以根据它对可用系统资源的了解来决定在通话时.

类似的劝告可以在他的网站上的C++ 11-FAQ中找到.

"简单"是async()/ future设计最重要的方面; 期货也可以用在一般线程使用,但是甚至不认为使用async()来启动该做的我的任务/ O,操纵互斥体,或者以其他方式与其他任务进行交互.

有趣的是,当他在他的书的第42.4.6节中更详细地回到C++ 11的并发特性时,他没有详细说明这个限制.更有趣的是,在本章中他实际上还在继续(与他网站上的陈述相比):

一个简单而实际的用法async()是生成一个任务来收集用户的输入.

asyncon 的文档cppreference.com根本没有提到任何这样的限制.

在阅读了导致最终形式的C++ 11标准的一些提议和讨论之后(遗憾的是我无法访问),我明白它async已经很晚才纳入C++ 11标准并且有很多讨论这个功能应该有多强大.特别是,我发现了文章中的Async Tasks in C++ 11:Not Quite There Yet Bartosz Milewski对实现时必须考虑的问题进行了非常好的总结async.

但是,所有讨论的问题都与thread_local如果线程被回收时未被破坏的变量,虚假死锁或数据访问违规有关,如果线程在操作中切换其任务并且两个任务分别持有mutexrecursive_mutex等等.这些都为特征的实现者严重关切,但如果我理解正确的话,目前规范async要求所有这些细节被执行或者在调用者线程或任务对用户隐藏的,如果作为一个新线程的任务创建.

所以我的问题是:我不允许做什么async,我被允许thread手动使用s,这个限制的原因是什么?

例如,以下程序有什么问题吗?

#include <future>
#include <iostream>
#include <mutex>
#include <vector>

static int tally {};
static std::mutex tally_mutex {};

static void
do_work(const int amount)
{
  for (int i = 0; i < amount; ++i)
    {
      // Might do something actually useful...
      const std::unique_lock<std::mutex> lock {tally_mutex};
      tally += 1;
    }
}

int
main()
{
  constexpr int concurrency {10};
  constexpr int amount {1000000};
  std::vector<std::future<void>> futures {};
  for (int t = 0; t < concurrency; ++t)
    futures.push_back(std::async(do_work, amount / concurrency));
  for (auto& future : futures)
    future.get();
  std::cout << tally << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

显然,如果运行时决定在主线程上安排所有任务,我们将毫无理由地一遍又一遍地不必要地获取互斥锁.但虽然这可能效率低下,但并非不正确.

Ant*_*ams 6

std :: async的"问题"是默认情况下你不知道它是否启动一个线程.如果你的函数需要在一个单独的线程上运行,那么这是一个问题,因为在调用get()或wait()之前你的函数可能不会运行.你可以传递std :: launch :: async来确保函数在它自己的线程上启动,这就像一个不能分离BG的std :: thread.