我正在尝试在C ++中创建一个boost线程,可以重用它来运行各种不同数量和类型的args的函数。
可以用C ++ 11x可变参数完成吗?
在我的用例中,我不需要队列(如果线程忙,那么方法就会失败),但如果需要实现这种“统一”功能,我会不情愿地这样做。
我不明白如何使用bind或lambda处理unify,以便一个线程可以调用不同的函数,每个函数都有自己的不同数量和类型的args。
我大致有以下几点:
class WorkThread
{
public:
WorkThread()
{
// create thread and bind runner to thread
}
~WorkThread()
{
// tell runner to exit and wait and reap the thread
}
template<typename F,typename ... Arguments>
void doWork(F func, Arguments... args)
{
if already busy
return false;
// set indication to runner that there is new work
// here: how to pass f and args to runner?
}
private:
void runner()
{
while ( ! time to quit )
{
wait for work
// here: how to get f and args from doWork? do I really need a queue? could wait on a variadic signal maybe?
f(args);
}
}
boost::thread* m_thread;
};
class ThreadPool
{
public:
template<typename F, typename ... Arguments>
bool doWork(F func,Arguments... args)
{
const int i = findAvailableWorkThread();
m_thread[i].doWork(f,args);
}
private:
// a pool of work threads m_thread;
};
答案 0 :(得分:2)
应该有很多现有问题显示如何执行此操作。
表示任意函数对象的规范C ++ 11方法是std::function<void()>
,因此您需要一个在下面的代码中称为m_job
的该类型的共享对象,该对象应该受到保护。互斥,并在尚未设置时为其分配新作业:
template<typename F,typename ... Arguments>
bool doWork(F func, Arguments&&... args)
{
std::lock_guard<std::mutex> l(m_job_mutex);
if (m_job)
return false;
m_job = std::bind(func, std::forward<Arguments>(args)...);
m_job_cond.notify_one();
return true;
}
这使用std::bind
将函数对象及其参数转换为不带参数的函数对象。 std::bind
返回的可调用对象存储func
和每个参数的副本,并在调用时调用func(args...)
然后工人就这样做了:
void runner()
{
while ( ! time to quit )
{
std::function<void()> job;
{
std::unique_lock<std::mutex> l(m_job_mutex);
while (!m_job)
m_job_cond.wait(l);
swap(job, m_job);
}
job();
}
}
此代码不是线程安全的:
template<typename F, typename ... Arguments>
bool doWork(F func,Arguments... args)
{
const int i = findAvailableWorkThread();
m_thread[i].doWork(f,args);
}
findAvailableWorkThread
返回后,该线程可能会变忙,因此下一行将失败。您应该检查可用性并通过单个操作传递新作业,例如
template<typename F, typename ... Arguments>
bool doWork(F func,Arguments... args)
{
for (auto& t : m_thread)
if (t.doWork(f,args))
return true;
return false;
}
答案 1 :(得分:1)
您可以使用boost::bind
或C ++ 11 lambda
将所有功能统一为一个签名。因此,您将拥有一个带循环函数的线程,在该循环内部,您将提取要执行的函数。
要实现它,您可以使用boost::lockfree::queue<boost::function<void()>>
,例如。
作为这个想法的一个例子,您可以使用以下内容:
class TaskLoop
{
public:
typedef std::function<void()> Task_t;
public:
TaskLoop():
m_IsDone(false)
{
m_spThread.reset(new std::thread(&TaskLoop::_run, this));
}
~TaskLoop()
{
Task_t task = [this](){m_IsDone = true;};
postTask(task);
m_spThread->join();
}
void postTask(const Task_t& Msg)
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Tasks.push(Msg);
}
void wait()
{
while(!m_Tasks.empty());
}
private:
bool m_IsDone;
std::unique_ptr<std::thread> m_spThread;
std::mutex m_Mutex;
std::queue<Task_t> m_Tasks;
private:
void _run()
{
while(!m_IsDone)
{
Task_t task;
m_Mutex.lock();
if(!m_Tasks.empty())
{
task = m_Tasks.front();
m_Tasks.pop();
}
m_Mutex.unlock();
if(task)
task();
}
}
};
void foo(const std::string& first, int second)
{
std::cout << first << second << "\n";
}
int main(int argc, char **argv)
{
TaskLoop loop;
loop.postTask([]{foo("task", 0);});
loop.wait();
return 0;
}
示例不使用并发队列而且非常简单,因此您需要替换队列并使其适应您的要求
答案 2 :(得分:1)
由于您已经在使用boost,请参阅http://think-async.com/Asio/Recipes处的线程池类,它使用Boost.Asio对工作函数进行排队而不会锁定。
答案 3 :(得分:0)
//这就是我到目前为止看起来有效......
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <queue>
#include <atomic>
class ThreadWorker
{
public:
typedef boost::function<void()> Task_t;
typedef std::unique_ptr<boost::thread> UniqueThreadPtr_t;
ThreadWorker()
: m_timeToQuit(false)
{
m_spThread = UniqueThreadPtr_t( new boost::thread(std::bind(&ThreadWorker::runner, this)));
}
virtual ~ThreadWorker()
{
quit();
}
void quit()
{
Task_t task = [this]() { m_timeToQuit = true; };
enqueue(task);
m_spThread->join();
}
template<typename F,typename ...Args>
void enqueue(F&& f, Args&&... args)
{
boost::mutex::scoped_lock lock(m_jobMutex);
m_Tasks.push(std::bind(std::forward<F>(f),std::forward<Args>(args)...));
m_newJob.notify_one();
}
private:
std::atomic<bool> m_timeToQuit;
UniqueThreadPtr_t m_spThread;
mutable boost::mutex m_jobMutex;
std::queue<Task_t> m_Tasks;
boost::condition m_newJob;
private:
void runner()
{
while ( ! m_timeToQuit )
{
Task_t task;
{
boost::mutex::scoped_lock lock(m_jobMutex);
while ( ! task )
{
if ( m_timeToQuit )
{
break;
}
if ( m_Tasks.empty() )
{
m_newJob.wait(lock);
}
task = m_Tasks.front();
m_Tasks.pop();
}
}
if (task)
{
task();
}
}
}
};