Jen*_*ens 6 c++ boost-thread boost-signals2
我必须编写一个执行高度计算密集型计算的程序.该程序可能会运行几天.可以在不同的线程中轻松分离计算,而无需共享数据.我想要一个GUI或一个Web服务,告诉我当前的状态.
我目前的设计使用BOOST :: signals2和BOOST :: thread.它编译并到目前为止按预期工作.如果一个线程完成一次迭代并且有新数据可用,则它会调用一个连接到GUI类中的插槽的信号.
我的问题:
以下代码编译
g++ -Wall -o main -lboost_thread-mt <filename>.cpp
Run Code Online (Sandbox Code Playgroud)
代码如下:
#include <boost/signals2.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <iterator>
#include <string>
using std::cout;
using std::cerr;
using std::string;
/**
* Called when a CalcThread finished a new bunch of data.
*/
boost::signals2::signal<void(string)> signal_new_data;
/**
* The whole data will be stored here.
*/
class DataCollector
{
typedef boost::mutex::scoped_lock scoped_lock;
boost::mutex mutex;
public:
/**
* Called by CalcThreads call the to store their data.
*/
void push(const string &s, const string &caller_name)
{
scoped_lock lock(mutex);
_data.push_back(s);
signal_new_data(caller_name);
}
/**
* Output everything collected so far to std::out.
*/
void out()
{
typedef std::vector<string>::const_iterator iter;
for (iter i = _data.begin(); i != _data.end(); ++i)
cout << " " << *i << "\n";
}
private:
std::vector<string> _data;
};
/**
* Several of those can calculate stuff.
* No data sharing needed.
*/
struct CalcThread
{
CalcThread(string name, DataCollector &datcol) :
_name(name), _datcol(datcol)
{
}
/**
* Expensive algorithms will be implemented here.
* @param num_results how many data sets are to be calculated by this thread.
*/
void operator()(int num_results)
{
for (int i = 1; i <= num_results; ++i)
{
std::stringstream s;
s << "[";
if (i == num_results)
s << "LAST ";
s << "DATA " << i << " from thread " << _name << "]";
_datcol.push(s.str(), _name);
}
}
private:
string _name;
DataCollector &_datcol;
};
/**
* Maybe some VTK or QT or both will be used someday.
*/
class GuiClass
{
public:
GuiClass(DataCollector &datcol) :
_datcol(datcol)
{
}
/**
* If the GUI wants to present or at least count the data collected so far.
* @param caller_name is the name of the thread whose data is new.
*/
void slot_data_changed(string caller_name) const
{
cout << "GuiClass knows: new data from " << caller_name << std::endl;
}
private:
DataCollector & _datcol;
};
int main()
{
DataCollector datcol;
GuiClass mc(datcol);
signal_new_data.connect(boost::bind(&GuiClass::slot_data_changed, &mc, _1));
CalcThread r1("A", datcol), r2("B", datcol), r3("C", datcol), r4("D",
datcol), r5("E", datcol);
boost::thread t1(r1, 3);
boost::thread t2(r2, 1);
boost::thread t3(r3, 2);
boost::thread t4(r4, 2);
boost::thread t5(r5, 3);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
datcol.out();
cout << "\nDone" << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
utn*_*tim 13
这种信号和线程的组合是一个明智的想法吗?我是另一个论坛,有人建议别人不要"走这条路".
这似乎是合理的.你能提供其他线程的链接吗?他们在解释他们的推理吗?
附近是否有潜在的致命陷阱,我没有看到?
如果他们是我也看不到他们.您需要注意的是通知是线程安全的(触发信号不会切换线程上下文,GuiClass::slot_data_changed应该从所有其他线程调用.
我期望的现实,这将是"容易"用我的GUI类来提供Web界面或QT,一个VTK或任何窗口?
没那么简单.要解决此问题,您必须使用通知切换线程上下文.这就是我要做的事情:
让你GuiClass成为一个抽象的基类,实现它自己的消息队列.当GuiClass::slot_data_changed您的线程调用时,您将锁定互斥锁并在内部(private:)消息队列上发布已接收通知的副本.在您的线程中GuiClass创建一个锁定互斥锁并在队列中查找通知的函数.此函数应该在客户端代码的线程中运行(在您从抽象中专门化的具体类的线程中GuiClass).
好处:
缺点:
您的客户端代码必须运行轮询方法或允许它运行(作为线程处理函数).
这有点复杂:)
我期望的现实,这将是"容易"用我的GUI类来提供Web界面或QT,一个VTK或任何窗口?
看不到,但这并不容易.除了线程上下文切换之外,我可能还有其他问题.
有没有更聪明的选择(像其他升压库),我忽略了?
不是其他的升级库,但是编写线程的方式并不好:连接是在代码中按顺序进行的.要只join为所有线程使用一个,请使用boost :: thread_group.
代替:
boost::thread t1(r1, 3);
boost::thread t2(r2, 1);
boost::thread t3(r3, 2);
boost::thread t4(r4, 2);
boost::thread t5(r5, 3);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
Run Code Online (Sandbox Code Playgroud)
你将会有:
boost::thread_group processors;
processors.create_thread(r1, 3);
// the other threads here
processors.join_all();
Run Code Online (Sandbox Code Playgroud)
编辑:线程上下文是特定于特定运行线程的所有内容(特定于线程的存储,该线程的堆栈,该线程的上下文中抛出的任何异常等).
当您在同一个应用程序(多个线程)中有各种线程上下文时,您需要同步访问在线程上下文中创建并从不同线程访问的资源(使用锁定原语).
例如,假设你有a一个class A[在线程tA中运行]做一些事情b的实例,以及class B[在线程tB的上下文中运行]的一个实例,并且b想要说出a一些东西.
"想要告诉a某事"部分意味着b想要调用a.something()并将a.something()在tB的上下文中调用(在线程B的堆栈上).
要更改此项(要a.something()在tA的上下文中运行),您必须切换线程上下文.这意味着,而不是b告诉a"运行A::something()",b讲述a"运行A ::东西()在`你自己的线程上下文".
经典实施步骤:
ba从tB内发送消息
a 来自tA内的消息的民意调查
当a从中发现消息时b,它在tA内运行a.something()本身.
这是线程上下文的切换(执行A::something将以tA而不是tB执行,就像直接从中调用一样b).
从您提供的链接,似乎已经实现了boost::asio::io_service,因此如果您使用它,您不必自己实现它.
有一个非常重要的陷阱:
据我所知,signal2的线程安全性,插槽在信令线程中运行.大多数GUI库(尤其是Qt和OpenGL)必须从单个线程完成所有绘图.一般来说这不是问题,但需要一点小心.您有两种选择:
首先,你要小心不要在里面做任何绘图GuiClass::slot_data_changed(因为你使用Qt看看QCoreApplication :: postEvent(抱歉不允许发布链接到Qt文档)).
第二个是你自己构建一个消息队列,它保存了插槽调用并在GUI线程中执行它们.这有点繁琐,但也更安全,因为您可以编写GUI类而无需关心线程安全性.
| 归档时间: |
|
| 查看次数: |
5477 次 |
| 最近记录: |