赞
踩
创建一个QThread和QWorker(继承自QObject)类对象,使用moveToThread函数移动到thread中运行,通过thread类start信号和worker的init槽函数绑定,init槽函数内是一些初始化操作,然后定义个定时器,周期触发doWork()。
网上有很多教程是在doWork()中使用while(isRunning)死循环的形式,不建议这么干,如果线程一直在doWork中死循环,那么他是无法接收到来自外部的信号的。推荐的方法是用定时器周期触发。
#ifndef WORKSERVER_H #define WORKSERVER_H #include <QObject> #include <QThread> #include <QTcpServer> #include <QTcpSocket> #include <QByteArray> #include <QDebug> class WorkServer : public QObject { Q_OBJECT public: explicit WorkServer(QObject *parent = nullptr); ~WorkServer(); signals: void sigRecv(QByteArray ba, quint32 cnt); private: QTcpServer* m_server; }; #endif // WORKSERVER_H
#include "workserver.h" WorkServer::WorkServer(QObject *parent) : QObject(parent) { static quint32 count = 0; qDebug()<<"WorkServer thread ID:"<<QThread::currentThreadId(); m_server = new QTcpServer(this); m_server->listen(QHostAddress::Any, 8888); connect(m_server, &QTcpServer::newConnection, this, [=](){ QTcpSocket* tcpsock = m_server->nextPendingConnection(); connect(tcpsock, &QTcpSocket::readyRead, this, [=](){ QByteArray ba = tcpsock->readAll(); count++; emit sigRecv(ba, count); tcpsock->write("5566"); // qDebug()<<"tcpsock recv:"<<ba; // qDebug()<<"WorkServer thread ID:"<<QThread::currentThreadId(); }); connect(tcpsock, &QTcpSocket::disconnected, this, [=](){ qDebug()<<"tcpsock disconnected..."; tcpsock->deleteLater(); qDebug()<<"WorkServer thread ID:"<<QThread::currentThreadId(); count = 0; }); }); } WorkServer::~WorkServer() { // m_server->deleteLater(); }
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QThread> #include "workserver.h" QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); public slots: void clientSend(QByteArray ba, quint32 cnt); private slots: void on_pushButton_clicked(); private: Ui::Widget *ui; QThread* m_threadServer; WorkServer* m_workServer; }; #endif // WIDGET_H
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); qDebug()<<"main thread ID:"<<QThread::currentThreadId(); m_threadServer = new QThread; //不要指定parent m_workServer = new WorkServer; //不要指定parent connect(m_workServer, &WorkServer::sigRecv, this, &Widget::clientSend); connect(m_threadServer, &QThread::finished, this, [=](){ m_threadServer->quit(); m_threadServer->deleteLater(); }); m_workServer->moveToThread(m_threadServer); m_threadServer->start(); } Widget::~Widget() { // m_threadServer->quit(); m_workServer->deleteLater(); delete ui; } void Widget::clientSend(QByteArray ba, quint32 cnt) { QString str(ba); QString str2 = QString::number(cnt); str += " cnt: " + str2; ui->te_Recv->append(str); } void Widget::on_pushButton_clicked() { ui->te_Recv->clear(); }
两种方法来执行线程都可以,随便你的喜欢。不过看起来第二种更加简单,容易让人理解。不过我们的兴趣在于这两种使用方法到底有什么区别?其最大的区别在于:
moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。
子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。如果你不明白的话,请看,第二个例子中,子类化的线程的槽函数中输出当前线程的ID,而这个ID居然是主线程的ID!!事实的确是如此,子类化的QThread只能执行run()函数中的任务直到run()函数退出,而它的槽函数根本不会被自己的线程执行。
为了同步线程,Qt提供了QMutex、QReadWriteLock、QSemaphore和QWaitCondition类。主线程等待与其他线程的中断时,必须进行同步。例如:两个线程同时访问共享变量,那么可能得不到预想的结果。因此,两个线程访问共享变量时,必须进行同步。
一个线程安全的函数不一定是可重入的;一个可重入的函数缺也不一定是线程安全的!
可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个线程调用此函数时,很有可能使有关全局变量变为不可知状态。
满足下列条件的函数多数是不可重入的:
也就是说:本质上,可重入性与C++类或者没有全局静态变量的函数相似,由于只能访问自身所有的数据变量区域,所以即使有两个以上线程访问,也可以保证安全性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。