您的问题有 2 个部分。存储作业列表并以线程安全的方式操作作业列表。
对于第一部分,请查看 std::function、std::bind 和 std::ref。
对于第二部分,这类似于生产者/消费者问题。您可以使用std::mutex和std::condition_variable实现信号量。
有一个提示/大纲。现在我的完整答案...
第 1 步)
将函数指针存储在 std::function 队列中。
std::queue<std::function<void()>>
队列中的每个元素都是一个不带参数并返回void的函数。
对于带参数的函数,使用std::bind 绑定参数。
void testfunc(int n);
...
int mynum = 5;
std::function<void()> f = std::bind(testfunction, mynum);
当 f 被调用时,即f(),5 将作为参数 1 传递给testfunc。 std::bind 立即按值复制 mynum。
您可能还希望能够通过引用传递变量。这对于从函数获取结果以及传递共享同步设备(如信号量和条件)很有用。使用 std::ref,引用包装器。
void testfunc2(int& n); // function takes n by ref
...
int a = 5;
std::function<void()> f = std::bind(testfunction, std::ref(a));
std::function 和 std::bind 可以与任何可调用对象(函数、函子或 lambda)一起使用——这非常简洁!
第 2 步)
当队列非空时,工作线程出队。您的代码应该类似于生产者/消费者问题。
class AsyncWorker
{
...
public:
// called by main thread
AddJob(std::function<void()> f)
{
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push(std::move(f));
++m_numJobs;
}
m_condition.notify_one(); // It's good style to call notify_one when not holding the lock.
}
private:
worker_main()
{
while(!m_exitCondition)
doJob();
}
void doJob()
{
std::function<void()> f;
{
std::unique_lock<std::mutex> lock(m_mutex);
while (m_numJobs == 0)
m_condition.wait(lock);
if (m_exitCondition)
return;
f = std::move(m_queue.front());
m_queue.pop();
--m_numJobs;
}
f();
}
...
注 1: 同步代码...与m_mutex、m_condition 和m_numJobs...本质上是在 C++'11 中实现信号量所必须使用的.我在这里所做的比使用单独的信号量类更有效,因为只有 1 个锁被锁定。 (信号量有自己的锁,您仍然需要锁定共享队列)。
注意 2:您可以轻松添加额外的工作线程。
注意 3: 在我的示例中,m_exitCondition 是 std::atomic<bool>
实际上以多态方式设置AddJob函数进入C++'11可变参数模板和完美转发...
class AsyncWorker
{
...
public:
// called by main thread
template <typename FUNCTOR, typename... ARGS>
AddJob(FUNCTOR&& functor, ARGS&&... args)
{
std::function<void()> f(std::bind(std::forward<FUNCTOR>(functor), std::forward<ARGS&&>(args)...));
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push(std::move(f));
++m_numJobs;
}
m_condition.notify_one(); // It's good style to call notify_one when not holding the lock.
}
我认为如果您只使用按值传递而不是使用转发引用,它可能会起作用,但我没有对此进行测试,虽然我知道完美的转发效果很好。避免完美转发可能会使概念稍微不那么混乱,但代码不会有太大不同......