我先说这是我第一次钻研多线程。尽管对并发和同步进行了大量阅读,但我并没有轻易找到满足我的要求的解决方案。
使用C ++ 11和Boost,我试图弄清楚如何将数据从工作线程发送到主线程。工作线程在应用程序启动时生成,并持续监视无锁队列。对象以不同的间隔填充此队列。这部分正在运作。
一旦数据可用,它就需要由主线程处理,因为另一个信号将被发送到不能在工作线程上的应用程序的其余部分。这就是我的意思我遇到了麻烦。
如果我必须通过互斥锁或条件变量阻塞主线程,直到工作线程完成,那将如何提高响应能力?我不妨留下一个线程,所以我可以访问数据。我必须在这里遗漏一些东西。
我发了几个问题,认为Boost :: Asio是要走的路。有一个例子说明如何在线程之间发送信号和数据,但正如响应所示,事情变得过于复杂并且不能完美地运行:
How to connect signal to boost::asio::io_service when posting work on different thread?
Boost::Asio with Main/Workers threads - Can I start event loop before posting work?
与一些同事交谈后,建议使用两个队列 - 一个输入,一个输出。这将在共享空间中,输出队列将由工作线程填充。工作线程总是在运行但是需要一个Timer,可能是在应用程序级别,它会强制主线程检查输出队列以查看是否有任何挂起的任务。
关于我应该引起注意的任何想法?是否有任何技术或策略可以用于我正在尝试做的事情?我接下来会看着计时器。
感谢。
编辑:这是对后处理模拟结果的插件系统的生产代码。我们首先使用C ++ 11,然后是Boost。我们正在使用Boost的lockfree :: queue。应用程序在单个线程上执行我们想要的操作,但现在我们正在尝试优化我们看到存在性能问题的位置(在这种情况下,通过另一个库进行计算)。主线程有很多责任,包括数据库访问,这就是我想限制工作线程实际做什么的原因。
更新:我已经成功使用std :: thread来启动一个工作线程来检查一个Boost lock :: free队列并处理放入它的任务。这是@Pressacco的第5步回答我遇到了麻烦。任何一个示例在工作线程完成时将值返回给主线程并通知主线程,而不是简单地等待工作者完成?
答案 0 :(得分:1)
如果你的目标是从头开始开发解决方案(使用本机线程,队列等):
Queue
+ Semaphore
+ WorkerThread
s 其他说明
如果您决定从头开始实现线程安全队列,请查看:
话虽如此,我还会再看看BOOST。我还没有使用过库,但据我所知,它很可能包含一些相关的数据结构(例如线程安全队列)。
我最喜欢的引用来自MSDN:
"当您使用任何类型的多线程时,您可能会暴露 你自己对非常严重和复杂的错误"
<强> SIDEBAR 强>
由于您是第一次查看并发编程,因此您可能需要考虑:
答案 1 :(得分:0)
上面的反馈使我朝着正确的方向前进,为我所需要的。该解决方案绝对比我之前尝试使用信号/插槽或Boost :: Asio更简单。我有两个无锁队列,一个用于输入(在工作线程上)和一个用于输出(在主线程上,由工作线程填充)。我使用计时器来安排何时处理输出队列。代码如下;也许这对某人有用:
//Task.h
#include <iostream>
#include <thread>
class Task
{
public:
Task(bool shutdown = false) : _shutdown(shutdown) {};
virtual ~Task() {};
bool IsShutdownRequest() { return _shutdown; }
virtual int Execute() = 0;
private:
bool _shutdown;
};
class ShutdownTask : public Task
{
public:
ShutdownTask() : Task(true) {}
virtual int Execute() { return -1; }
};
class TimeSeriesTask : public Task
{
public:
TimeSeriesTask(int value) : _value(value) {};
virtual int Execute()
{
std::cout << "Calculating on thread " << std::this_thread::get_id() << std::endl;
return _value * 2;
}
private:
int _value;
};
// Main.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "afxwin.h"
#include <boost/lockfree/spsc_queue.hpp>
#include "Task.h"
static UINT_PTR ProcessDataCheckTimerID = 0;
static const int ProcessDataCheckPeriodInMilliseconds = 100;
class Manager
{
public:
Manager()
{
//Worker Thread with application lifetime that processes a lock free queue
_workerThread = std::thread(&Manager::ProcessInputData, this);
};
virtual ~Manager()
{
_workerThread.join();
};
void QueueData(int x)
{
if (x > 0)
{
_inputQueue.push(std::make_shared<TimeSeriesTask>(x));
}
else
{
_inputQueue.push(std::make_shared<ShutdownTask>());
}
}
void ProcessOutputData()
{
//process output data on the Main Thread
_outputQueue.consume_one([&](int value)
{
if (value < 0)
{
PostQuitMessage(WM_QUIT);
}
else
{
int result = value - 1;
std::cout << "Final result is " << result << " on thread " << std::this_thread::get_id() << std::endl;
}
});
}
private:
void ProcessInputData()
{
bool shutdown = false;
//Worker Thread processes input data indefinitely
do
{
_inputQueue.consume_one([&](std::shared_ptr<Task> task)
{
std::cout << "Getting element from input queue on thread " << std::this_thread::get_id() << std::endl;
if (task->IsShutdownRequest()) { shutdown = true; }
int result = task->Execute();
_outputQueue.push(result);
});
} while (shutdown == false);
}
std::thread _workerThread;
boost::lockfree::spsc_queue<std::shared_ptr<Task>, boost::lockfree::capacity<1024>> _inputQueue;
boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024>> _outputQueue;
};
std::shared_ptr<Manager> g_pMgr;
//timer to force Main Thread to process Manager's output queue
void CALLBACK TimerCallback(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
if (nIDEvent == ProcessDataCheckTimerID)
{
KillTimer(NULL, ProcessDataCheckPeriodInMilliseconds);
ProcessDataCheckTimerID = 0;
//call function to process data
g_pMgr->ProcessOutputData();
//reset timer
ProcessDataCheckTimerID = SetTimer(NULL, ProcessDataCheckTimerID, ProcessDataCheckPeriodInMilliseconds, (TIMERPROC)&TimerCallback);
}
}
int main()
{
std::cout << "Main thread is " << std::this_thread::get_id() << std::endl;
g_pMgr = std::make_shared<Manager>();
ProcessDataCheckTimerID = SetTimer(NULL, ProcessDataCheckTimerID, ProcessDataCheckPeriodInMilliseconds, (TIMERPROC)&TimerCallback);
//queue up some dummy data
for (int i = 1; i <= 10; i++)
{
g_pMgr->QueueData(i);
}
//queue a shutdown request
g_pMgr->QueueData(-1);
//fake the application's message loop
MSG msg;
bool shutdown = false;
while (shutdown == false)
{
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
shutdown = true;
}
}
return 0;
}