QCM学习—基于QT自制上位机(多线程)

发布于:2022-11-11 ⋅ 阅读:(1101) ⋅ 点赞:(0)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言:应用程序在某些情况下需要处理比较复杂的逻辑,例如常规的图传上位机,如果在传输图片跑到较高码流或对图像执行一些处理任务是,引用多线程可以明显改善响应度和反馈速度。

  • QT中所包含的线程类
  • 多线程的使用方式
  • QT线程池的使用


QT多线程使用的注意事项:

1.QT的默认线程为主线程:负责窗口事件处理或窗口控件数据的更新;

2.子线程不能对窗口对象做任何操作;

3.主线程依托信号槽机制与子线程之间进行数据传递;


提示:以下是本篇文章正文内容,下面案例可供参考

一、QT线程类:QThread

建议先过一遍 QThread Class 文档。

使用方法

1.引入库

new QThread Class & Override run()

/*------------------------------WorkerThread-----------------------------------*/
class WorkerThread : public QThread
{
    Q_OBJECT
public:
    explicit WorkerThread();
protected:
    void run();
signals:
    void resultReady(const QString &s);
};

void WorkerThread::run(){
    /* ... here is the expensive or blocking operation ... */
}

/*------------------------------MainWindow-----------------------------------*/
void MainWindow::startWorkInAThread()
{
    WorkerThread *workerThread = new WorkerThread();
    // Release object in workerThread
    connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
    workerThread->start();
}
run()中未调用exec(),那么在run()结束后,线程自动退出;
在上述例程中,WorkThread存在于实例化它的旧线程中,仅有run()是在子线程中执行,验证方法:
qDebug()<<"mythread QThread :: currentThreadId()==" << QThread::currentThreadId()
;

该案例也说明QThread本身并不是一个线程,QThread Class 是一个线程管理器。 

new Object Class & moveToThread(new Qtread)

/*------------------------------DisconnectMonitor-----------------------------------*/
class DisconnectMonitor : public QObject{
        Q_OBJECT
public:
        explicit DisconnectMonitor();
signals:
        void StartMonitor(long long hanlde);
        void StopMonitor();
        void Disconnect();
private slots:
        void slot_StartMonitor(long long hanlde);
        void slot_StopMonitor();
        void Monitor();
private:
        long long ControllerHanlde;
        QTimer * MonitorTimer;
};

DisconnectMonitor::Disconnectmonitor()
{
        Monitor Timer= new QTimer;
        ControllerHanlde = 0;
        connect(MonitorTimer, &Qtimer::timeout, this, &DisconnectMonitor::Monitor);
        connect(this, &DisconnectMonitor::StartMonitor, this, &DisconnectMonitor::slot_StartMonitor);
        connect(this, &DisconnectMonitor::StopMonitor, this, &DisconnectMonitor::slot_StopMonitor);
        MonitorTimer->start(TAKETIME);
}
void DisconnectMonitor::Monitor(){
        if(ControllerHanlde == 0){
                return;
        }
        else{
                int state = IsConnect(ControllerHanlde);
                if(state != 0){
                        emit Disconnect();
                }
        }
}
class Controller : public QObiect{
        Q_OBJECT
        QThread workerThread;
public:
        Controller(){
                DisconnectMonitor *monitor = new DisconnectMonitor;
                monitor->moveToThread(&workerThread);
                connect(workerThread, &QThreads::finished, monitor, &QObject::deleteLater);
                connect(monitor, SIGNAL(Disconnec t()), this, SLOT(DisconnectManage()));
                workerThread.start();
        }
        ~Ciontroller(){
                workerThread.quit();
                workerThread.wait();
        }
private slots:
        voidDisconnectManage();
};

在该例程中的DisconnectMonitor 中定义了一个定时器用以实现定时检测。在上述例程中,WorkThread存在于实例化它的旧线程中,仅有run()是在子线程中执行,验证方法:
qDebug()<<"mythread QThread :: currentThreadId()==" << QThread::currentThreadId()
;

该案例也说明QThread本身并不是一个线程,QThread Class 是一个线程管理器。在上述案例中并不能认为monitor的控制权归属于新线程,movetoThread()的作用是将槽函数在指定的的线程中调用,仅有槽函数在指定线程中调用,包括槽函数在指定线程中调用,包括构造函数都仍在主线程调用 。

DisconnectMonitor 须继承自顶层父类Object,否则不能移动;

如果Thread 为nullptr, 则该对象机器子对象的所有事件处理都将停止(与线程不再关联);

调用moveThread()时,移动对象的所有计时器都将被重置。且它只能将一个对象“推”到另一个线程,不能将对象从任意线程推到当前线程(该对象与其他线程有关联)。

2.开启线程

代码如下(示例):使用QThread->start()开启线程

/*-----------------------qthread_win.cpp---------------------------------*/
void QThread::start(Priority priority)
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex);
    if (d->isInFinish) {
        locker.unlock();
        wait();
        locker.relock();
    }

    if (d->running)
        return;
    d->running = true;
    d->finished = false;
    d->exited = false;
    d->returnCode = 0;
    d->interruptionRequested = false;

    // 【1】判断当前环境,调用系统API创建线程 d->handle 为线程句柄
    #if defined(Q_CC_MSVC) && !defined(_DLL) // && !defined(Q_OS_WINRT)
    #  ifdef Q_OS_WINRT
    #    error "Microsoft documentation says this combination leaks memory every time a thread is started. " \
        "Please change your build back to -MD/-MDd or, if you understand this issue and want to continue, " \
        "edit this source file."
    #  endif
        // MSVC -MT or -MTd build
        d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
                                                this, CREATE_SUSPENDED, &(d->id));
    #else
        // MSVC -MD or -MDd or MinGW build
        d->handle = CreateThread(nullptr, d->stackSize,
                                reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
                                this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
    #endif // Q_OS_WINRT
    //创建线程失败
    if (!d->handle) {
        qErrnoWarning("QThread::start: Failed to create thread");
        d->running = false;
        d->finished = true;
        return;
    }
    //优先级
    int prio;
    d->priority = priority;
    switch (d->priority) {
    case IdlePriority:
        prio = THREAD_PRIORITY_IDLE;
        break;
    case LowestPriority:
        prio = THREAD_PRIORITY_LOWEST;
        break;
    case LowPriority:
        prio = THREAD_PRIORITY_BELOW_NORMAL;
        break;
    case NormalPriority:
        prio = THREAD_PRIORITY_NORMAL;
        break;
    case HighPriority:
        prio = THREAD_PRIORITY_ABOVE_NORMAL;
        break;
    case HighestPriority:
        prio = THREAD_PRIORITY_HIGHEST;
        break;
    case TimeCriticalPriority:
        prio = THREAD_PRIORITY_TIME_CRITICAL;
        break;
    case InheritPriority:
    default:
        prio = GetThreadPriority(GetCurrentThread());
        break;
    }
    if (!SetThreadPriority(d->handle, prio)) {
        qErrnoWarning("QThread::start: Failed to set thread priority");
    }
    if (ResumeThread(d->handle) == (DWORD) -1) {
        qErrnoWarning("QThread::start: Failed to resume new thread");
    }
}

3.退出线程

首先,删除QThread对象并不会停止起管理的线程执行。删除正在运行的QThread将导致程序崩溃,在删除之前我们须等待finish信号。

1.对于未开启事件循环的线程,我们仅需让run()执行结束即可终止线程。

void TestThread::stopThread(){
        mutex.lock();
        runenable = false;
        mutex.unlock();
}
void TestThread::run(){
        runenable = true;
        while(1){
                if(mutex.tryLock()){
                        if(!runenable)
                                break;
                }
        }
}

2.对于开启事件循环的线程,我们应该操作的是退出事件循环。

  • 若线程开启了EvenLoop,我们可调用 quit()/exit() + wait() 实现退出。
  • 调用terminate()后,由于线程可能在任何位置中值,强制结束线程很危险:可能修改数据、可能导致线程状态无法清除、可能导致锁异常。因此终止后仍须使用wait()。
  • 以上手段军存在内存泄漏的情况,我们可以使用finish信号。

二、线程优先级的设定

在QThread中我们可以通过setPriority()设置线程优先级,通过priority()获取线程优先级。

QThread还提供了sleep()\msleep()\usleep()静态函数,进行短暂的线程屏蔽。

三、QT线程类:QThread

在大多情况下,线程必须停止以等待其他线程。

使用方法

1.互斥锁 

QMutex 任意时刻至多有一个线程可以使用该锁(常用于数据共享)

QMutex mutex;
void thread1(){
        mutex.lock();
        mutex.unlock();
}

2.读写锁

QReadWriteLock 允许多个线程对数据共享进行读取,可提高多线程程序的并发度。


void ReadThread::run(){
        lock.lockForRead();
        read_file();
        lock.unlock();
}
void ReadThread::run(){         
        lock.lockForWrite();
        write_file();
        lock.unlock();
 }

四、线程合理开启

线程数量=可用核心数量/(1 - 阻塞系数)

最简单的办法是  cpu核心数*2+2

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到