【问题标题】:boost:asio thread pool implementation for occasionally synchronized tasksboost:asio 线程池实现,用于偶尔同步的任务
【发布时间】:2012-10-20 05:33:36
【问题描述】:

我有一个“主要”功能,每个时间步执行许多小的独立任务。但是,在每个时间步之后,我必须等待所有任务完成才能继续前进。

我想让程序多线程。我已经尝试过使用 boost-offshoot 线程池的实现,我尝试过使用(共享指针)线程的向量,我已经尝试过 asio 线程池的想法(使用 io_service,建立一些工作,然后将运行分发到线程和向 io_service 发布处理程序)。

所有这些似乎都有很多开销为我的“许多小任务”创建和销毁线程,我想要一种方法,最好使用 asio 工具,实例化一个 io_service,一个 thread_group,将处理程序发布到 io_service ,并等待单个时间步的工作完成,然后再发布更多任务。有没有好的方法来做到这一点?这是我现在工作的(精简)代码:

boost::asio::io_service io_service;
for(int theTime = 0; theTime != totalTime; ++theTime)
{
    io_service.reset();
    boost::thread_group threads;
    // scoping to destroy the work object after work is finished being assigned
    {
        boost::asio::io_service::work work(io_service);
        for (int i = 0; i < maxNumThreads; ++i)
        {
            threads.create_thread(boost::bind(&boost::asio::io_service::run,
                &io_service));
        }

        for(int i = 0; i < numSmallTasks; ++i)
        {
            io_service.post(boost::bind(&process_data, i, theTime));
        }
    }
    threads.join_all(); 
}

这是我宁愿拥有的(但不知道如何实现):

boost::asio::io_service io_service;
boost::thread_group threads;
boost::asio::io_service::work work(io_service);
for (int i = 0; i < maxNumThreads; ++i)
{
    threads.create_thread(boost::bind(&boost::asio::io_service::run,
         &io_service));
}

for(int theTime = 0; theTime != totalTime; ++theTime)
{
    for(int i = 0; i < numSmallTasks; ++i)
    {
        io_service.post(boost::bind(&process_data, i, theTime));
    }
    // wait here until all of these tasks are finished before looping 
    // **** how do I do this? *****
}
// destroy work later and join all threads later...

【问题讨论】:

  • 这不是在“时间”for循环中调用io_service.stop()那么简单,在所有任务都发布之后,是吗?文档似乎没有表明所有发布的处理程序都将在停止之前执行...

标签: c++ multithreading gcc boost boost-asio


【解决方案1】:

您可以使用futures 进行数据处理并使用boost::wait_for_all() 与它们同步。这将允许您根据已完成工作的部分而不是线程进行操作。

int process_data() {...}

// Pending futures
std::vector<boost::unique_future<int>> pending_data;

for(int i = 0; i < numSmallTasks; ++i)
{
   // Create task and corresponding future
   // Using shared ptr and binding operator() trick because
   // packaged_task is non-copyable, but asio::io_service::post requires argument to be copyable

   // Boost 1.51 syntax
   // For Boost 1.53+ or C++11 std::packaged_task shall be boost::packaged_task<int()>
   typedef boost::packaged_task<int> task_t;

   boost::shared_ptr<task_t> task = boost::make_shared<task_t>(
      boost::bind(&process_data, i, theTime));

   boost::unique_future<int> fut = task->get_future();

   pending_data.push_back(std::move(fut));
   io_service.post(boost::bind(&task_t::operator(), task));    
}

// After loop - wait until all futures are evaluated
boost::wait_for_all(pending_data.begin(), pending_data.end()); 

【讨论】:

  • 好的,这听起来很有希望。我需要研究期货。感谢您为我指明了这个新方向。
  • Rost,我在没有 std::move 的情况下无法实现这个(我被困在 C++03 编译器上)。如何使用 std::move?
  • @JohnDoe 尝试改用boost::move
  • 在C++03中使用std::auto_ptr代替std::unique_ptr,它的复制构造函数实际上是C++03中的移动构造函数
  • 好的,我已经完成了一半...我使用 shared_future 代替 std::vector(以及来自 task.get_future() 的 shared_future 的复制构造函数)。但是我一生都无法弄清楚如何将 packaged_task 发布到 io_service (是时候提出一个新问题了)。这似乎是正确的方法,所以我会将您的标记为最佳答案。感谢大家的帮助!
【解决方案2】:

也许你可以使用boost::barrier如下:

void thread_proc( boost::barrier& b ) {
    while( true ) {
        if( !ioservice.run_one() ) break; // io_service stopped
        b.wait();
    }
}

【讨论】:

  • 有趣。如果我可以为每个线程设置某种条件来检查是否有更多的处理程序发布到每个线程的队列,我也许可以使用屏障。有这种事吗?
  • 你可能有:while( true ) { if( i_should_do_action() &amp;&amp; !ioservice.run_once() ) break; b.wait(); }
  • 我不确定我是否理解。实例化“工作”对象后,io_service 将永远不会处于停止状态,对吗?也就是说,在上面的设置中,对 io_service.run_once() 的调用是否会返回 0?
  • 这是一个糟糕的解决方案。这是不正确的,不可扩展的,违背了多线程的想法。如果线程数远少于要完成的部分工作怎么办?我想是的。每个线程将执行一部分工作并阻塞,直到所有其他线程完成。但为什么???它必须处理另一件工作!
  • 另外考虑你有 N 个线程和 M 个工作部分。 N
【解决方案3】:

Rost 的方法本质上是有效的,但是 boost::make_shared 不能按原样编译。以下是工作版本(在 vs2012 中):

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/thread.hpp>

std::vector<boost::unique_future<void>> pending_data;
typedef boost::packaged_task<void> task_t;

boost::shared_ptr< boost::packaged_task<void> > pt(new boost::packaged_task<void> ([&,i](){...}));
boost::unique_future<void> result = pt->get_future();
pending_data.push_back(boost::move(result));
io_service.post(boost::bind(&task_t::operator(), pt));

boost::wait_for_all(pending_data.begin(), pending_data.end()); 
pending_data.clear();

如果在 packaged_task typedef 中使用参数,它将无法编译。这个由 asio 和 future 方法创建的线程池与每个循环创建新线程方法相比仅节省了 8% 的时间。

【讨论】:

    猜你喜欢
    • 2015-07-13
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 2012-10-29
    • 2014-01-31
    • 1970-01-01
    相关资源
    最近更新 更多