题
我已要求这个问题在许多受欢迎的论坛,但没有具体的响应。我的火车乘客应采用串行通信的接口与外部系统的每一个都具有其自身的接口议定书》。接收到的数据的系统显示在上一个GUI在脱4.2.1.
结构应用程序是这样的
当的应用程序的开始,我们有一个登录网页 有一个选择的四个模块。此 实现为maindisplay 类。每个模块是 单独的类本身。有关模块是这里的行动类是负责收集和显示数据可从各种系统。
用户认证得到他/她的 进行动画面。的 构造的行动的屏幕 类的执行,并除了 凡初始这开始的 单个的系统,是 实现为单例。
每个系统的协议是实现为一个单独线的形式:
class SensorProtocol:public QThread {
static SensorProtocol* s_instance;
SensorProtocol(){}
SensorProtocol(const SensorProtocol&);
operator=(const SensorProtocol&);
public:
static SensorProtocol* getInstance();
//miscellaneous system related data to be used for
// data acquisition and processing
};
在实现文件*.cpp:
SensorProtocol* SensorProtocol::s_instance=0;
SensorProtocol* SensorProtocol::getInstance()
{
//DOUBLE CHECKED LOCKING PATTERN I have used singletons
// without this overrated pattern also but just fyi
if(!s_instance)
{
mutex.lock();
if(!s_instance)
s_instance=new SensorProtocol();
mutex.unlock();
}
}
结构的功能运行
while(!mStop)
{
mutex.lock()
while(!WaitCondition.wait(&mutex,5)
{
if(mStop)
return;
}
//code to read from port when data becomes available
// and process it and store in variables
mutex.unlock();
}
在行动的屏幕上班我有义InputSignalHandler使用sigaction和saio.这是一个功能指针,它被激活,尽快数据达的任何序列港口。
它是一个全球性的功能(我们不能改变它,因为它是具体到Linux),只是用来比较的文件的描述符串口数据已经到来,fd的传感器系统,如果找到匹配将.wakeOne调用在线程和它出来的等待和读取和处理数据。
在行动的屏幕类的个别的线都开始 SensorProtocol::getInstance()->start()
.
每个系统的协议有一个框架速度发送的数据。基于这一事实,在行动的屏幕我们设置了新的计时器到时候在刷新率的协议。当这些计时器时出UpdateSensorProtocol()功能操作的屏幕是所谓
connect(&timer, SIGNAL(timeout), this,SLOT(UpdateSensorProtocol()));
该抓住的一个实例,传感器单独作为
SensorProtocol* pSingleton=SensorProtocol::getInstance();
if(pSingleton->mUpdate)
{
//update data on action screen GUI
pSingleton->mUpdate=false; //NOTE : this variable is set to
// true in the singleton thread
// while one frame is processed completely
}
对于所有使用的单独实例 SensorProtocol::getInstance()
使用。鉴于上述情况,我的一个协议是挂着无论有什么变化我做的。
挂起发生在同时显示的数据使用UpdateSensorProtocol()如果我的评论 ShowSensorData()
功能 UpdateSensorProtocol()
它工作正常。但是,否则它挂起和GUI冻结。任何建议。
此外,由于主线抓住的运行实例的单,这真的是多线程,因为我们基本上是改变mUpdate在单独本身尽管是从操作的屏幕。
我搞不清楚这一点。
此外,有人可以建议一个备用的设计以什么我现在所做的。
在此先感谢
解决方案
首先,不要让系统单身。使用某种上下文封装 针对不同的系统。
如果你忽略了这个建议,仍然想创建“单身人士”。线程至少使用 QApplication :: instance();
作为线程的父级,并在单例析构函数中放入 QThread :: wait()
,否则程序将崩溃程序退出。
if(!s_instance){
QMutexLocker lock(&mutex);
if(!s_instance)
s_instance=new SensorProtocol( QApplication::instance());
}
但这不会解决你的问题...
Qt是事件驱动的,所以尝试部署这个非常好的事件驱动架构并为每个系统线程创建一个事件循环。然后你可以创建“SystemProtocols”。生活在另一个线程中,您可以创建计时器,在线程之间发送事件,...而不使用低级同步对象。点击
看看Bradley T. Hughes的博客文章没有头痛的踩踏
代码没有编译,但应该让你知道从哪里开始......
class GuiComponent : public QWidget {
//...
signals:
void start(int); // button triggerd signal
void stop(); // button triggerd singal
public slots:
// don't forget to register DataPackage at the metacompiler
// qRegisterMetaType<DataPackage>();
void dataFromProtocol( DataPackage ){
// update the gui the the new data
}
};
class ProtocolSystem : public QObject {
//...
int timerId;
signals:
void dataReady(DataPackage);
public slots:
void stop() {
killTimer(timerId);
}
void start( int interval ) {
timerId = startTimer();
}
protected:
void timerEvent(QTimerEvent * event) {
//code to read from port when data becomes available
// and process it and store in dataPackage
emit dataReady(dataPackage);
}
};
int main( int argc, char ** argv ) {
QApplication app( argc, argv );
// construct the system and glue them together
ProtocolSystem protocolSystem;
GuiComponent gui;
gui.connect(&protocolSystem, SIGNAL(dataReady(DataPackage)), SLOT(dataFromProtocol(DataPackage)));
protocolSystem.connect(&gui, SIGNAL(start(int)), SLOT(start(int)));
protocolSystem.connect(&gui, SIGNAL(stop()), SLOT(stop()));
// move communication to its thread
QThread protocolThread;
protocolSystem.moveToThread(&protocolThread);
protocolThread.start();
// repeat this for other systems ...
// start the application
gui.show();
app.exec();
// stop eventloop to before closing the application
protocolThread.quit();
protocolThread.wait();
return 0;
}
现在你有完全独立的系统,gui和协议现在彼此不相同,甚至不知道程序是多线程的。您可以在单个线程环境中独立地对所有系统进行单元测试,并在实际应用程序中将它们粘合在一起,如果需要,可以在不同的线程之间进行划分。点击 这是我将用于此问题的程序体系结构。没有单个低级同步元素的多线程。没有竞争条件,没有锁,......
其他提示
问题:
使用RAII,锁您的互斥体。他们目前不例外安全。
while(!mStop)
{
mutex.lock()
while(!WaitCondition.wait(&mutex,5))
{
if(mStop)
{
// PROBLEM 1: You mutex is still locked here.
// So returning here will leave the mutex locked forever.
return;
}
// PROBLEM 2: If you leave here via an exception.
// This will not fire, and again you will the mutex locked forever.
mutex.unlock();
// Problem 3: You are using the WaitCondition() incorrectly.
// You unlock the mutex here. The next thing that happens is a call
// WaitCondition.wait() where the mutex MUST be locked
}
// PROBLEM 4
// You are using the WaitCondition() incorrectly.
// On exit the mutex is always locked. So nwo the mutex is locked.
什么代码应该是这样的:
while(!mStop)
{
MutextLocker lock(mutex); // RAII lock and unlock mutex.
while(!WaitCondition.wait(&mutex,5))
{
if(mStop)
{
return;
}
//code to read from port when data becomes available
// and process it and store in variables
}
通过使用RAII它解决了所有问题我发现上面。
在一个侧面的注意。
你的双检查锁定不会正常工作。
通过使用静态功能的变量建议通过'安德斯*卡尔松你解决问题,因为克++保证静态功能的变量将只是初始化的一次。此外,这一方法保证singelton将被正确地销毁(通过析构).目前,除非你是在做一些花哨的东西通过onexit()你会被泄漏的记忆。
在这里看到许多细节约更好地执行单例。
C++单独的设计图案
看到这里为什么你的双检查锁定不起作用。
什么是所有的共同未定义的行为C++编程应该知道的吗?
我首先使用RAII(资源获取初始化)来提高锁定代码的安全性。您的代码如下所示:
mutex.lock();
...logic...
mutex.unlock();
将互斥锁代码包含在ctor中获取互斥锁并在dtor中释放的类中。现在您的代码如下所示:
MyMutex mutex;
...logic...
主要的改进是,如果逻辑部分有任何异常,你的互斥锁仍会被释放。
另外,不要让任何异常泄漏出你的线程!即使您不知道如何处理它们,除了在某处记录它们之外,请抓住它们。
我不能完全确定问题是什么,因为我不知道 ShowSensorData()
函数(方法?)正在做什么,但是你的代码有一些多线程问题包括在内。
-
mUpdate
如果被多个线程访问,则应受互斥锁保护。 -
run()
方法看起来会锁定互斥锁,如果mStop
为真,则永远不会释放它。
醇>
您应该考虑使用 RAII 做法来获取和释放互斥锁。我不知道您是否使用Qt互斥量,但您应该考虑使用 QMutexLocker 锁定和解锁互斥锁。
我会考虑更改你的 SensorProtocol
类来使用条件变量和一个标志或某种事件(不知道Qt在这里提供什么)来处理与之相关的方法内的更新对象实例。类似的东西:
/*static*/ void
SensorProtocol::updateSensorProtocol() {
SensorProtocol *inst = SensorProtocol::getInstance();
inst->update();
}
然后确保 update()
方法在读取或写入读取器和显示器之间共享的任何成员之前抓取互斥锁。
更完整的方法是使用模型 - 视图 - 控制器架构。将解决方案重构为MVC架构可能会简化相当多的事情。更不用说它使这样的应用程序更容易出错。请查看 QAbstractItemView 和 QAbstractItemDelegate 类,了解如何实现此功能。根据我的记忆,有一个关于在某个地方使用Qt实现MVC的教程......自从我使用Qt以来已经有好几年了。
你的getInstance方法也可以像这样写,以避免使用s_instance var:
SensorProtocol& getInstance()
{
static SensorProtocol instance;
return instance;
}
双重检查锁定模式在C ++中被破坏。这在整个互联网上都有很好的记录。我不知道你的问题是什么,但显然你需要在你的代码中解决这个问题。
查看 QextSerialPort :
QextSerialPort是一个跨平台的 串口类。这个班 封装两者上的串口 POSIX和Windows系统。
QextSerialPort 继承自 QIODevice ,使串口通信与Qt API的其余部分更加顺畅地集成。
此外,您可以使用消息传递方案在I / O和GUI线程之间进行通信,而不是共享内存。这通常不易出错。您可以使用 QApplication :: postEvent 功能将自定义QEvent消息发送到使用 QObject :: customeEvent 处理程序在GUI线程中处理QObject。它将为您处理同步并缓解您的死锁问题。
这是一个快速而又肮脏的例子:
class IODataEvent : public QEvent
{
public:
IODataEvent() : QEvent(QEvent::User) {}
// put all of your data here
};
class IOThread : public QThread
{
public:
IOThread(QObject * parent) : QThread(parent) {}
void run()
{
for (;;) {
// do blocking I/O and protocol parsing
IODataEvent *event = new IODataEvent;
// put all of your data for the GUI into the event
qApp->postEvent(parent(), event);
// QApplication will take ownership of the event
}
}
};
class GUIObject : public QObject
{
public:
GUIObject() : QObject(), thread(new IOThread(this)) { thread->start() }
protected:
void customEvent(QEvent *event)
{
if (QEvent::User == event->type) {
IODataEvent *data = (IODataEvent *) event;
// get data and update GUI here
event->accept();
} else {
event->ignore();
}
// the event loop will release the IODataEvent memory automatically
}
private:
IOThread *thread;
};
另外,Qt 4 支持排队信号和插槽跨线程。
有三个单独的线程用于发送,接收和显示。
每当收到数据时提出一个事件并在显示线程中处理它。
编辑以回复评论1
我承认我对qt一无所知但是从你所说的内容看来你仍然可以创建你的串口对象,而后者又启动了两个工作线程(通过使用start方法)输入和输出缓冲控制。
如果串口类具有“连接到端口”,获得串口使用的方法; “开放端口”启动工作线程并打开端口的方法; “关闭端口”关闭发送和接收线程的方法以及用于设置“On Data Received”的属性。事件处理程序然后你应该全部设置。
这个类不应该是一个单例,因为你会发现大多数操作系统在任何时候都不允许多个进程控制一个串口,而是你会得到一个异常(你需要它当你尝试连接时,如果它已经在使用中。工作线程确保端口保持在您的控制之下。