使用std :: launch :: async参数对由std :: async启动的线程感到困惑

我对std::async函数有点困惑。

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

现在,这是什么意思?

在我的理解中,代码

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

应该在一个新的线程上启动函数pow2 ,并通过值将该变量num传递给线程,然后在将来某个时候,当函数完成时,将结果放在fut (只要函数pow2具有像double pow2(double);这样的签名double pow2(double); )。 但规格说明“仿佛”,这使整个事情有点雾对我来说。

问题是:

在这种情况下总是启动一个新线程? 但愿如此。 我的意思是对于我来说,参数std::launch::async是有道理的,我明确指出我确实想要创建一个新线程。

和代码

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

应该通过延迟pow2函数调用到我写类似var = fut.get();地方来进行懒惰评估 var = fut.get(); 。 在这种情况下, std::launch::deferred参数应该表示我明确指出,我不想要一个新线程,我只是想确保函数在需要返回值时被调用。

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

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

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

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


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

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

  • std::launch::async指示函数调用必须在其自己的(新)线程上运行。 (考虑到用户@TC的评论)。
  • std::launch::deferred表示函数调用将被延迟,直到将来调用wait()get() 。 在这种情况发生之前,未来的所有权可以转移到另一个线程。
  • std::launch::async | std::launch::deferred std::launch::async | std::launch::deferred表示实现可以选择。 这是默认选项(如果您自己没有指定)。 它可以决定同步运行。
  • 在这种情况下总是启动一个新线程?

    1开始 ,我们可以说总是启动一个新线程。

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

    2开始 ,我们可以说你的假设是正确的。

    这是什么意思? [关于正在启动或未启动的新线程)

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

    警告

    以下部分与您的问题无关,但我认为务必牢记。

    C ++标准说,如果一个std::future对调用异步函数的共享状态的最后一个引用,std :: future的析构函数必须阻塞,直到异步运行函数的线程完成。 因此std::async返回的std::future实例会在其析构函数中被阻塞。

    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::async返回的std::future实例是临时的,并且会被阻塞,因为它们的析构函数在std::async返回时被调用,因为它们没有被分配给变量。

    第一次调用std::async将会阻塞2秒,然后再从第二次调用阻塞到std::async 。 我们可能认为最后一次调用std::async不会阻塞,因为我们将返回的std::future实例存储在一个变量中,但是由于这是一个本地变量,它在范围末尾被销毁,它实际上会在局部变量f被破坏时,在函数范围的末尾再封锁2秒。

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

    我用来编制这些注释的信息来源:

    C ++ Concurrency in Action:实用多线程,Anthony Williams

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


    我也对此感到困惑,并在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
    

    事实并非如此。 添加thread_local存储值,你会看到,实际上std::async run f1 f2 f3在不同线程中std::async run f1 f2 f3任务,但是使用相同的std::thread::id

    链接地址: http://www.djcxy.com/p/30813.html

    上一篇: Confusion about threads launched by std::async with std::launch::async parameter

    下一篇: Can I use std::async without waiting for the future limitation?