C++ 给定一个期货容器,如何以非阻塞方式执行所有GET?

C++ 给定一个期货容器,如何以非阻塞方式执行所有GET?,c++,c++11,future,stdasync,C++,C++11,Future,Stdasync,因此,我试图创建一种通用方法,既可以创建未来容器,也可以以非阻塞方式执行所有future.get() 我预计任务的完成时间应该从几百毫秒到2分钟不等。然而,有些可能根本不完整。在一次典型运行中,至少要执行10000个任务 我希望能够最快地返回任务结果,而不会被futures容器中其他更长期运行的任务耽搁 到目前为止,我只使用虚拟睡眠时间来模拟任务完成延迟(设计在很大程度上要感谢这里发表的好文章,例如,和): #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 大小和范围(常数大小、常

因此,我试图创建一种通用方法,既可以创建未来容器,也可以以非阻塞方式执行所有future.get()

我预计任务的完成时间应该从几百毫秒到2分钟不等。然而,有些可能根本不完整。在一次典型运行中,至少要执行10000个任务

我希望能够最快地返回任务结果,而不会被futures容器中其他更长期运行的任务耽搁

到目前为止,我只使用虚拟睡眠时间来模拟任务完成延迟(设计在很大程度上要感谢这里发表的好文章,例如,和):

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
大小和范围(常数大小、常数大小);
int rand_sleep_范围(常数大小,常数大小);
模板大小\u t获取\u异步\u全部(CT&);
//给定一个函数和一个集合,
//返回一个未来向量。
模板
自动异步(函数f、CT coll)
->向量
{
向量期货;
期货储备(coll.size());
用于(自动和元素:coll)
futures.push_back(std::async(f,element));
收益期货;
}
//给定一个数字的开头和结尾
//范围,返回其中的随机数。
大小和范围(常量大小和范围开始),
常数大小(范围结束)
{
标准:均匀分布
Disr(范围开始,范围结束);
std::随机_设备开发;
返回分发(开发);
} 
//给定最短和最长的持续时间,将呼叫
//线程在其中随机睡眠一段时间。
//(以毫秒为单位)
int rand_sleep_范围(常数大小最短时间),
常数大小(最长时间)
{
std::chrono::毫秒
睡眠时间(随机范围(最短时间、最长时间));
std::this_线程::sleep_for(sleep_time);
return(int)sleep_time.count();
} 
//给定一个未来容器,执行所有
//获取()。
模板
大小\u t获取\u异步\u全部(CT和异步\u coll)
{
大小不能获取中心(0);
const size\u t future\u cnt=async\u coll.size();
std::向量完成;
已完成。保留区(未来);
while(true){
对于(尺寸ndx=0;ndxstd::cout至少有一个问题。您调用
std::async
而不指定启动策略,这意味着部分或所有任务可能会延迟运行。但在测试任务是否已完成时,您只测试
std::future\u status\u ready
。如果任务延迟,您将始终返回
std::future\utatus_deferred
,这意味着您的测试将永远不会返回true

这个问题最简单的解决方案是指定一个启动策略
std::launch::async
,但这样会有超额订阅系统的风险。另一种方法是修改测试以检查延迟的任务,但问题是如何处理它们。如果对它们调用
get
wait
,则会阻止它们任意时间

关于一般方法,而不是阻塞10ms以等待每个任务在轮询它们时完成,您可以考虑等待0ms,即,执行纯投票以查看任务是否完成。这可以减少任务完成和处理过程之间的延迟,但它可能会增加轮询到点的位置。整个系统运行较慢


一种完全不同的方法可能是放弃轮询每个任务,而是让每个任务向共享数据结构(例如,
std::deque
)写入一个“我完成了”标志,然后定期轮询该数据结构以查看其中是否有任何内容。如果是,则处理已完成的任务,将它们从数据结构中删除,然后返回睡眠状态,直到再次轮询。如果您的任务对数据结构执行
推回
,您可以自然地按照完成的顺序处理它们。这种设计的一个缺点是共享数据结构可能成为性能瓶颈。

好吧,您的代码没有多少非阻塞性,因为它会阻塞到最后一个将来完成。但是我认为如果没有类似
then()
(这不是C++11中的)的东西,就无法做得更好@svick噢,我想我明白你的意思了。嗯。我想最重要的是当一个任务完成时,它可以立即返回给调用方,而剩余的任务会在适当的时候完成。如果我创建了一个deque,我也通过ref传递给get_async_all(),然后从内部将它向后推,并从内部推一个完整的未来If()语句,这种方法有效吗?也许我可以让一个输出线程池接受它?我计划将此算法作为库的一部分实现,如果这有什么不同的话。感谢您的输入!如果任务已完成,正确的测试是
。请等待(std::chrono::seconds(0))
#include <future>
#include <vector>
#include <iostream>
#include <random>
#include <chrono>
#include <ratio>
#include <thread>
#include <algorithm>

size_t rand_from_range(const size_t, const size_t);
int rand_sleep_range(const size_t, const size_t);
template<class CT> size_t get_async_all( CT& );

// Given a function and a collection,
//  return a vector of futures.
template<class Function, class CT>
auto async_all( Function f, CT coll )
    -> std::vector<decltype(std::async(f, *std::begin(coll)))>
{
  std::vector<decltype(std::async(f, *std::begin(coll)))> futures;
  futures.reserve(coll.size());
  for (auto& element : coll)
    futures.push_back(std::async(f, element));
  return futures;
}

// Given the beginning and end of a number
//  range, return a random number therein.
size_t rand_from_range( const size_t range_begin, 
                        const size_t range_end )
{
  std::uniform_int_distribution<size_t> 
    distr(range_begin, range_end);
  std::random_device dev;
  return distr(dev);
} 

// Given a shortest and longest duration, put the calling
//  thread to sleep for a random duration therein. 
// (in milliseconds)
int rand_sleep_range( const size_t shortest_time, 
                      const size_t longest_time )
{
  std::chrono::milliseconds 
    sleep_time(rand_from_range(shortest_time, longest_time));
  std::this_thread::sleep_for(sleep_time);
  return (int)sleep_time.count();
} 

// Given a container of futures, perform all
//  get()'s.
template<class CT>
size_t get_async_all( CT& async_coll )
{
  size_t get_ctr(0);
  const size_t future_cnt = async_coll.size();
  std::vector<size_t> completed;
  completed.reserve(future_cnt);

  while (true) {
    for (size_t ndx = 0; ndx < future_cnt; ++ndx) {
      // Check to see if this ndx' future has completed already.
      if (std::none_of(std::begin(completed), std::end(completed), 
            [=](size_t x) {
              return (x == ndx);
            }))
      { // No, this one hasn't completed 
        //  yet, attempt to process it.
        auto& f = async_coll[ndx];
        if (f.wait_for(std::chrono::milliseconds(10)) 
              == std::future_status::ready) 
        {
          f.get(); // The future's work gets done here.
          ++get_ctr;
          completed.push_back(ndx);
          if (completed.size() == future_cnt) 
            break; // for()
        }
      }
    }
    if (completed.size() == future_cnt) 
      break; // while()
  }
  return get_ctr;
}

int main()
{
  // A dummy container of ints.
  std::vector<int> my_vec(100);
  for (auto& elem : my_vec)
    elem = rand_from_range(1, 100);

  // A dummy function lambda.
  auto my_func = [](int x) { 
    int x_ = x;
    int sleep_time = rand_sleep_range(100, 20000); // in ms.
    x *= 2;
    std::cout << " after sleeping " << sleep_time << "ms \t"
              << "f(" << x_ << ") = " << x << std::endl;
  };

  // Create and execute the container of futures.
  auto async_coll = async_all(my_func, my_vec);
  size_t count = get_async_all(async_coll);

  std::cout << std::endl << count << " items completed. \n";
}