为什么从std :: packaged_task和std :: async返回的std :: future不同? [英] Why std::future is different returned from std::packaged_task and std::async?

查看:80
本文介绍了为什么从std :: packaged_task和std :: async返回的std :: future不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我了解了从 std :: async 返回的 future 具有某种特殊的共享状态的原因,通过这种共享状态,等待返回的未来在未来的破坏者中.但是,当我们使用 std :: pakaged_task 时,它的未来不会表现出相同的行为.要完成打包任务,必须从 packaged_task future 对象上显式调用 get().

I got to know the reason that future returned from std::async has some special shared state through which wait on returned future happened in the destructor of future. But when we use std::pakaged_task, its future does not exhibit the same behavior. To complete a packaged task, you have to explicitly call get() on future object from packaged_task.

现在我的问题是:

  1. 将来的内部实现是什么(考虑 std :: async std :: packaged_task )?
  2. 为什么没有对从 std :: packaged_task 返回的 future 应用相同的行为?或者换句话说,对于 std :: packaged_task future 来说,相同的行为如何停止?
  1. What could be the internal implementation of future (thinking std::async vs std::packaged_task)?
  2. Why the same behavior was not applied to future returned from std::packaged_task? Or, in other words, how is the same behavior stopped for std::packaged_task future?

要查看上下文,请参见下面的代码:

To see the context, please see the code below:

它不等待完成 countdown 任务.但是,如果我取消注释//int value = ret.get(); ,它将完成 countdown ,这很明显,因为我们实际上阻止了返回的未来./p>

It does not wait to finish countdown task. However, if I un-comment // int value = ret.get();, it would finish countdown and is obvious because we are literally blocking on returned future.

    // packaged_task example
#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for

// count down taking a second for each value:
int countdown (int from, int to) {
  for (int i=from; i!=to; --i) {
    std::cout << i << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  std::cout << "Lift off!" <<std::endl;
  return from-to;
}

int main ()
{
   std::cout << "Start " << std::endl;
  std::packaged_task<int(int,int)> tsk (countdown);   // set up packaged_task
  std::future<int> ret = tsk.get_future();            // get future

  std::thread th (std::move(tsk),10,0);   // spawn thread to count down from 10 to 0

//   int value = ret.get();                  // wait for the task to finish and get result

  std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";

  th.detach();   

  return 0;
}

如果我使用 std :: async 在另一个线程上执行任务 countdown ,则无论我在返回的 future 对象或 not ,它将始终完成任务.

If I use std::async to execute task countdown on another thread, no matter if I use get() on returned future object or not, it will always finish the task.

// packaged_task example
#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for

    // count down taking a second for each value:
    int countdown (int from, int to) {
      for (int i=from; i!=to; --i) {
        std::cout << i << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      std::cout << "Lift off!" <<std::endl;
      return from-to;
    }
    
    int main ()
    {
       std::cout << "Start " << std::endl;
      std::packaged_task<int(int,int)> tsk (countdown);   // set up packaged_task
      std::future<int> ret = tsk.get_future();            // get future
    
      auto fut = std::async(std::move(tsk), 10, 0);   

    
    //   int value = fut.get();                  // wait for the task to finish and get result
    
      std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";

      return 0;
    }

推荐答案

std :: async 对执行给定任务的方式和位置有一定的了解.那就是它的工作:执行任务.为此,它实际上必须放在某个地方.某个地方可能是线程池,新创建的线程,或者在销毁 future 的任何人执行的地方.

std::async has definite knowledge of how and where the task it is given is executed. That is its job: to execute the task. To do that, it has to actually put it somewhere. That somewhere could be a thread pool, a newly created thread, or in a place to be executed by whomever destroys the future.

由于 async 知道函数的执行方式,因此它拥有100%的信息,需要该信息来构建一种机制,该机制可以在潜在的异步执行结束时进行通信,并确保如果销毁 future ,那么无论执行该功能的机制如何,最终都会真正执行它.毕竟,它知道该机制是什么.

Because async knows how the function will be executed, it has 100% of the information it needs to build a mechanism that can communicate when that potentially asynchronous execution has concluded, as well as to ensure that if you destroy the future, then whatever mechanism that's going to execute that function will eventually get around to actually executing it. After all, it knows what that mechanism is.

但是 packaged_task 没有.packaged_task 所做的全部工作就是存储一个可调用对象,该对象可以使用给定参数进行调用,使用函数的返回值类型创建一个 promise ,并提供一种获取两者的方法 future 并执行生成值的函数.

But packaged_task doesn't. All packaged_task does is store a callable object which can be called with the given arguments, create a promise with the type of the function's return value, and provide a means to both get a future and to execute the function that generates the value.

任务的实际执行时间和地点与 packaged_task 无关.没有这些知识,就无法建立使未来的析构函数与任务同步所需的同步.

When and where the task actually gets executed is none of packaged_task's business. Without that knowledge, the synchronization needed to make future's destructor synchronize with the task simply can't be built.

假设您要在新创建的线程上执行任务.好的,因此要使其执行与 future 的销毁同步,您需要一个互斥量,析构函数将阻塞该互斥量,直到任务线程完成为止.

Let's say you want to execute the task on a freshly-created thread. OK, so to synchronize its execution with the future's destruction, you'd need a mutex which the destructor will block on until the task thread finishes.

但是,如果您想在与 future 的析构函数的调用方相同的线程中执行任务,该怎么办?好吧,那么您不能使用互斥锁进行同步,因为它们都在同一线程上.相反,您需要使析构函数调用任务.这是一种完全不同的机制,并且取决于您计划执行的方式.

But what if you want to execute the task in the same thread as the caller of the future's destructor? Well, then you can't use a mutex to synchronize that since it all on the same thread. Instead, you need to make the destructor invoke the task. That's a completely different mechanism, and it is contingent on how you plan to execute.

因为 packaged_task 不知道您打算如何执行它,所以它无法执行任何操作.

Because packaged_task doesn't know how you intend to execute it, it cannot do any of that.

请注意,这并非 packaged_task 所独有.通过用户创建的 promise 对象创建的 All future 不会具有 async 未来 s.

Note that this is not unique to packaged_task. All futures created from a user-created promise object will not have the special property of async's futures.

所以问题实际上应该是为什么 async 以这种方式工作,而不是为什么其他所有人都不.

So the question really ought to be why async works this way, not why everyone else doesn't.

如果您想知道这一点,那是因为两个相互竞争的需求: async 必须是一种高级的,容易让人脑筋急转弯的异步执行方法(为此,需要进行同步破坏)很有道理),没有人愿意创建一个新的 future 类型,该类型与现有的析构函数的行为相同,只是现有的类型相同.因此,他们决定超载 future 的工作方式,从而使其实现和使用变得复杂.

If you want to know that, it's because of two competing needs: async needed to be a high-level, brain-dead simple way to get asynchronous execution (for which sychronization-on-destruction makes sense), and nobody wanted to create a new future type that was identical to the existing one save for the behavior of its destructor. So they decided to overload how future works, complicating its implementation and usage.

这篇关于为什么从std :: packaged_task和std :: async返回的std :: future不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆