关于std :: async使用std :: launch :: async参数

时间:2015-06-12 18:55:11

标签: c++ multithreading c++11 future stdthread

我对std::async函数感到有些困惑。

规范说:正在执行异步操作“就像在新的执行线程中一样”(C ++11§30.6.8/ 11)。

现在,这意味着什么?

根据我的理解,代码

std::future<double> fut = std::async(std::launch::async, pow2, num);

应该在新线程上启动函数pow2并按值传递变量num,然后在将来的某个时候,当函数完成时,将结果放在{{1 (只要函数fut具有类似pow2的签名)。但规范说“似乎”,这使整个事情对我来说有点模糊。

问题是:

在这种情况下是否始终会启动新主题?我希望如此。我的意思是对我来说,参数double pow2(double);是有道理的,我明确说明我确实要创建一个新线程。

代码

std::launch::async
通过将std::future<double> fut = std::async(std::launch::deferred, pow2, num); 函数调用延迟到我写pow2之类的点,

应该使延迟评估成为可能。在这种情况下,参数var = fut.get();应该意味着我明确说明,我不想要一个新线程,我只是想确保在需要它的返回值时调用该函数。

我的假设是否正确?如果没有,请解释。

另外,我知道默认情况下该函数调用如下:

std::launch::deferred

在这种情况下,我被告知是否会启动新线程取决于实现。再一次,这意味着什么?

3 个答案:

答案 0 :(得分:40)

std::async<future>标头的一部分)功能模板用于启动(可能)异步任务。它返回一个std::future对象,最终将保存std::async参数函数的返回值。

当需要该值时,我们在std::future实例上调用get();这将阻塞线程,直到将来准备就绪,然后返回值。可以将std::launch::asyncstd::launch::deferred指定为std::async的第一个参数,以指定任务的运行方式。

  1. std::launch::async表示函数调用必须在其自己的(新)线程上运行。 (将用户@ T.C.的评论考虑在内)。
  2. std::launch::deferred表示函数调用将被推迟,直到将来调用wait()get()为止。在此之前,未来的所有权可以转移到另一个线程。
  3. std::launch::async | std::launch::deferred表示实施可以选择。这是默认选项(如果您自己未指定)。它可以决定同步运行。
  4. 在这种情况下是否始终启动新主题?

    1。,我们可以说始终会启动新主题。

    我的假设[on std :: launch :: deferred]是否正确?

    2。,我们可以说您的假设是正确的。

    这是什么意思? [关于正在启动的新线程,取决于实现]

    3。,因为std::launch::async | std::launch::deferred是默认选项,这意味着模板函数std::async的实现将决定它是否会创建新线程。这是因为某些实现可能会检查过度调度。

    警告

    以下部分与您的问题无关,但我认为请务必记住这一点。

    C ++标准规定,如果std::future保存对与异步函数调用相对应的共享状态的最后一个引用,则std :: future的析构函数必须阻塞,直到异步运行函数的线程结束。因此std::future返回的std::async实例将在其析构函数中阻塞。

    void operation()
    {
        auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); };
        std::async( std::launch::async, func );
        std::async( std::launch::async, func );
        std::future<void> f{ std::async( std::launch::async, func ) };
    }
    

    这种误导性代码可能会让您认为std::async调用是异步的,它们实际上是同步的。 std::future返回的std::async个实例是临时的,并且会阻塞,因为在std::async返回时它们的析构函数被调用,因为它们未被赋值给变量。

    std::async的第一次呼叫将阻止2秒,然后在第二次呼叫std::async时再阻止2秒。我们可能认为对std::async的最后一次调用没有阻塞,因为我们将它返回的std::future实例存储在一个变量中,但由于这是一个在范围末尾被销毁的局部变量,所以当局部变量f被破坏时,实际上会在函数范围结束时再阻塞2秒。

    换句话说,调用operation()函数将阻塞同步调用的任何线程大约6秒。未来版本的C ++标准可能不存在这些要求。

    我用来编辑这些笔记的信息来源:

    C ++并发行动:实用多线程,Anthony Williams

    Scott Meyers的博文:http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html

答案 1 :(得分:0)

我也对此感到困惑,并在Windows上进行了快速测试,显示异步未来将在OS线程池线程上运行。一个简单的应用程序可以证明这一点,在Visual Studio中突破也将显示名为“TppWorkerThread”的执行线程。

#include <future>
#include <thread>
#include <iostream>

using namespace std;

int main()
{
    cout << "main thread id " << this_thread::get_id() << endl;

    future<int> f1 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f1.get(); 

    future<int> f2 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f2.get();

    future<int> f3 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f3.get();

    cin.ignore();

    return 0;
}

将导致输出类似于:

main thread id 4164
future run on thread 4188
future run on thread 4188
future run on thread 4188

答案 2 :(得分:0)

事实并非如此。 添加thread_local存储值,您会看到,实际上std::async run f1 f2 f3个任务位于不同的线程中,但具有相同的std::thread::id