从主线程C 11执行函数调用的线程
thread that executes function calls from a main thread c++11
我想实现一个可以从主线程中接受函数指针并串行执行的线程。我的想法是使用一个将功能指针及其对象保持的结构,并继续将其推到队列。这可以封装在班级中。然后,任务线程可以从队列中弹出并进行处理。我还需要同步它(因此它不会阻止主线程吗?(,所以我正在考虑使用信号量。尽管我对程序的结构有一个不错的想法,但我在编码此编码方面遇到了麻烦,尤其是C 11中的线程和信号量同步。如果有人可以提出一个我可以实施此功能的大纲,那就太好了。
编辑:重复的问题回答了有关创建线程池的问题。看来正在创建多个线程来完成一些工作。我只需要一个可以排队函数指针并按照收到的顺序进行处理的线程。
检查此代码段,我已经实现了不使用类。看看它是否有帮助。可以在这里避免有条件变量,但是我希望读者线程仅在作者发出信号时才进行轮询,以便不会浪费读者中的CPU周期。
#include <iostream>
#include <functional>
#include <mutex>
#include <thread>
#include <queue>
#include <chrono>
#include <condition_variable>
using namespace std;
typedef function<void(void)> task_t;
queue<task_t> tasks;
mutex mu;
condition_variable cv;
bool stop = false;
void writer()
{
while(!stop)
{
{
unique_lock<mutex> lock(mu);
task_t task = [](){ this_thread::sleep_for(chrono::milliseconds(100ms)); };
tasks.push(task);
cv.notify_one();
}
this_thread::sleep_for(chrono::milliseconds(500ms)); // writes every 500ms
}
}
void reader()
{
while(!stop)
{
unique_lock<mutex> lock(mu);
cv.wait(lock,[]() { return !stop;});
while( !tasks.empty() )
{
auto task = tasks.front();
tasks.pop();
lock.unlock();
task();
lock.lock();
}
}
}
int main()
{
thread writer_thread([]() { writer();} );
thread reader_thread([]() { reader();} );
this_thread::sleep_for(chrono::seconds(3s)); // main other task
stop = true;
writer_thread.join();
reader_thread.join();
}
您的问题有2个部分。以线程安全方式存储工作列表并操纵作业列表。
在第一部分中,查看std::function
,std::bind
和std::ref
。
在第二部分中,这类似于生产者/消费者问题。您可以使用std::mutex
和std::condition_variable
实现信号量。
有一个提示/轮廓。现在我的完整答案...
步骤1(
将您的功能指针存储在std ::功能的队列中。
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
可以与任何可呼叫(功能函数或lambdas(一起使用 - 这很整洁!
步骤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 variadic模板和完美的转发...
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.
}
我认为,如果您只是使用逐个值而不是使用转发引用,但我尚未对此进行测试,而我知道完美的转发非常有效。避免完美的转发可能会使概念略有混乱,但代码不会有很大不同...
- 线程 std::调用未知类型,无法专门化函数错误
- 在不同的线程中调用向量析构函数或清除
- 具有多线程支持的 RenderClass,将函数调用推送到向量以在另一个线程上调用
- 为什么我在C++线程函数调用中得到重复值?
- OpenCV 函数在由C++线程程序调用的 Python 脚本本身调用时锁定
- 在多线程中调用带有输入参数的函数
- 创建线程并调用类的函数
- 使用 boost 将成员函数调用为线程函数
- 必须在其他线程上调用 DLL 函数
- 在不同的线程上调用和执行函数
- 如何线程可调用函数谁是类的方法
- 在单独的线程中调用boost::python::object作为函数
- 线程的调用堆栈中充满了相同的函数调用--curl_inet_ntop()
- 为什么函子的 dtor 在作为函数的参数传递给线程时调用两次(多次)
- 通过引用使用参数进行 C++ 多线程函数调用
- 在从基类构造函数创建的单独线程中调用纯虚函数
- 线程函数未被调用.语法有什么问题吗
- 在不同线程中调用std::函数
- 为什么要将互斥对象作为参数传递给线程正在调用的函数
- 构造线程时调用的操作符重载函数