最近做项目,与仿真机通信。仿真机发送数据频率为1毫秒,导致Qt上位机在主线程频繁接收数据,造成界面卡死,因此将整个udp的通信和解析都放在线程中进行。
connect函数最后一个参数指定了三种连接方式:自动模式、直连模式和队列模式。自动模式下,是使用的直连模式还是队列模式,主要看信号发出者和槽接收者是否在同一线程。
直连模式: 是指当信号发送时,槽函数直接被调用,不管reciver是那个线程创建的,都在发射信号的线程内执行。换句话说,在子线程中创建sender对象,发送信号后,不管reciver是在主线程还是子线程创建,都会在子线程中执行。
#include
#include
#include
#include
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(){
}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_main()
{
qDebug()<<"from thread slot_main:" <<currentThreadId();
}
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
Dummy dummy;
QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main()));
thread.start();
dummy.emitsig();
return a.exec();
}
上面代码中,dummy和thread分别作为sender和reciver,都在同一个线程创建,采用直连模式,直接调用,所以slot_main()在主线程中执行。结果如下:
main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48
注意: 若想让slot_main()在子线程中执行,只需要将thread move到子线程中,改变thread的所在线程,采用队列模式。即将上面代码中“movetoThread(this);”解开注释即可。这种将线程对象移动到子线程的方案是强烈不推荐的。
队列模式: 是指sender发送信号后,信号被存到信号队列中,程序继续执行。直到reciver所在的线程事件循环处理到该信号时,槽函数被调用。槽函数在reciver对象所在的线程内执行。
#include
#include
#include
#include
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent){
}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_thread()
{
qDebug()<<"from thread slot_thread:" <<currentThreadId();
}
signals:
void sig();
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
Dummy dummy;
connect(&dummy, SIGNAL(sig()), this, SLOT(slot_thread()));
dummy.emitsig();
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
thread.start();
return a.exec();
}
代码中,thread在主线程中创建,dummy在子线程中创建,采用的是队列模式,所以槽函数slot_thread()其实在主线程中运行。解决方法有两个:
定义一个普通的QObject派生类,然后将其对象move到QThread中。使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。
#include
#include
#include
#include
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent) {
}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Object:public QObject
{
Q_OBJECT
public:
Object(){
}
public slots:
void slot()
{
qDebug()<<"from thread slot:" <<QThread::currentThreadId();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
QThread thread;
Object obj;
Dummy dummy;
obj.moveToThread(&thread);
QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot()));
thread.start();
dummy.emitsig();
return a.exec();
}
在线程中创建UDPserver,接收数据并解析,解析完毕后,发送信号给主线程,进行界面更新。
子线程:
void HandleDatasThread::run()
{
qDebug()<<"zixiancheng:"<<QThread::currentThreadId();
QUdpSocket server; //用于接收数据
quit = false;
server.bind(port, QUdpSocket::ShareAddress);
while(!quit)
{
if (server.hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(int(server.pendingDatagramSize()));
server.readDatagram(datagram.data(), datagram.size());
qDebug()<<datagram.toHex();
parseRecvData(datagram);
emit updatetable();
}
}
}
主线程:
connect(&m_calculationThread,SIGNAL(updatetable()),this,SLOT(computingFinished()));
void MainWindow::computingFinished()
{
recvVars = m_calculationThread.getVars();
if(QDateTime::currentMSecsSinceEpoch() / 1000 -lasttime>2)
{
setTableWidget(ui->recvTableWidget, recvVars);
lasttime = QDateTime::currentMSecsSinceEpoch() / 1000;
}
}
子线程中发送信号,主线程中处理,因此是队列模式,槽函数会在主线程中运行,从而更新界面。
通过普通的Object,movetothread的方式暂不介绍,有时间再整理。