我使用boost::asio
和一些调用boost::thread
的{{1}}个对象实现了一个线程池。但是,我得到的一个要求是有办法监视所有线程的“健康”。我的目的是创建一个可以通过线程池传递的简单的sentinel对象 - 如果它通过,那么我们可以假设该线程仍在处理工作。
但是,鉴于我的实现,我不确定如何(如果)我可以可靠地监视池中的所有线程。我只是将线程函数委托给boost::asio::io_service::run()
,因此将一个sentinel对象发布到boost::asio::io_service::run()
实例中将无法保证哪个线程实际上会获得该哨兵并完成工作。
一个选项可能是定期插入标记,并希望每个线程在一段合理的时间内至少拾取一次,但这显然不是理想的。
采用以下示例。由于处理程序编码的方式,在这个例子中我们可以看到每个线程将执行相同数量的工作,但实际上我将无法控制处理程序实现,一些可以长时间运行而其他人将几乎即时
io_service
答案 0 :(得分:6)
您可以在所有线程之间使用公共io_service实例,并为每个线程使用私有io_service实例。每个线程都会执行如下方法:
void Mythread::threadLoop()
{
while(/* termination condition */)
{
commonIoService.run_one();
privateIoService.run_one();
commonConditionVariable.timed_wait(time);
}
}
通过这种方式,如果您想确保某个任务在一个线程中执行,您只需要在其拥有的io_service中发布此任务。
要在线程池中发布任务,您可以执行以下操作:
void MyThreadPool::post(Hander handler)
{
commonIoService.post(handler);
commonConditionVariable.notify_all();
}
答案 1 :(得分:2)
我使用的解决方案依赖于我拥有胎面池对象的实现这一事实。我创建了一个包装器类型,它将更新统计信息,并复制发布到线程池的用户定义的处理程序。只有这个包装器类型才会发布到基础io_service
。这种方法允许我跟踪发布/执行的处理程序,而不必侵入用户代码。
这是一个精简的简化示例:
#include <iostream>
#include <memory>
#include <vector>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
// Supports scheduling anonymous jobs that are
// executable as returning nothing and taking
// no arguments
typedef std::function<void(void)> functor_type;
// some way to store per-thread statistics
typedef std::map<boost::thread::id, int> thread_jobcount_map;
// only this type is actually posted to
// the asio proactor, this delegates to
// the user functor in operator()
struct handler_wrapper
{
handler_wrapper(const functor_type& user_functor, thread_jobcount_map& statistics)
: user_functor_(user_functor)
, statistics_(statistics)
{
}
void operator()()
{
user_functor_();
// just for illustration purposes, assume a long running job
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
// increment executed jobs
++statistics_[boost::this_thread::get_id()];
}
functor_type user_functor_;
thread_jobcount_map& statistics_;
};
// anonymous thread function, just runs the proactor
void thread_func(boost::asio::io_service& proactor)
{
proactor.run();
}
class ThreadPool
{
public:
ThreadPool(size_t thread_count)
{
threads_.reserve(thread_count);
work_.reset(new boost::asio::io_service::work(proactor_));
for(size_t curr = 0; curr < thread_count; ++curr)
{
boost::thread th(thread_func, boost::ref(proactor_));
// inserting into this map before any work can be scheduled
// on it, means that we don't have to look it for lookups
// since we don't dynamically add threads
thread_jobcount_.insert(std::make_pair(th.get_id(), 0));
threads_.emplace_back(std::move(th));
}
}
// the only way for a user to get work into
// the pool is to use this function, which ensures
// that the handler_wrapper type is used
void schedule(const functor_type& user_functor)
{
handler_wrapper to_execute(user_functor, thread_jobcount_);
proactor_.post(to_execute);
}
void join()
{
// join all threads in pool:
work_.reset();
proactor_.stop();
std::for_each(
threads_.begin(),
threads_.end(),
[] (boost::thread& t)
{
t.join();
});
}
// just an example showing statistics
void log()
{
std::for_each(
thread_jobcount_.begin(),
thread_jobcount_.end(),
[] (const thread_jobcount_map::value_type& it)
{
std::cout << "Thread: " << it.first << " executed " << it.second << " jobs\n";
});
}
private:
std::vector<boost::thread> threads_;
std::unique_ptr<boost::asio::io_service::work> work_;
boost::asio::io_service proactor_;
thread_jobcount_map thread_jobcount_;
};
struct add
{
add(int lhs, int rhs, int* result)
: lhs_(lhs)
, rhs_(rhs)
, result_(result)
{
}
void operator()()
{
*result_ = lhs_ + rhs_;
}
int lhs_,rhs_;
int* result_;
};
int main(int argc, char **argv)
{
// some "state objects" that are
// manipulated by the user functors
int x = 0, y = 0, z = 0;
// pool of three threads
ThreadPool pool(3);
// schedule some handlers to do some work
pool.schedule(add(5, 4, &x));
pool.schedule(add(2, 2, &y));
pool.schedule(add(7, 8, &z));
// give all the handlers time to execute
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
std::cout
<< "x = " << x << "\n"
<< "y = " << y << "\n"
<< "z = " << z << "\n";
pool.join();
pool.log();
}
输出:
x = 9
y = 4
z = 15
Thread: 0000000000B25430 executed 1 jobs
Thread: 0000000000B274F0 executed 1 jobs
Thread: 0000000000B27990 executed 1 jobs