精华内容
下载资源
问答
  • 2021-02-03 17:08:57

    Qt线程 工作线程与GUI线程通信

    1、信号槽方式

    1)在子线程中定义界面组件的更新信号

    2)在主窗口类中定义更新界面组件的槽函数

    3)使用异步方式连接更新信号到槽函数

    ​ 子线程通过发送信号的方式更新界面组件,所有的界面组件对象只能依附于GUI线程(主线程)。子线程更新界面状态的本质是子线程发送信号通知主线程界面更新请求,主线程根据具体信号以及信号参数对界面组件进行修改。

    ​ 代码示例:上一篇线程实现方式二

    2、发送自定义事件方式

    1)自定义事件用于描述界面更新细节

    2)在主窗口类中重写事件处理函数event

    3)使用postEvent函数(异步方式)发送自定义事件类对象

    ​ 子线程指定接收消息的对象为主窗口对象,在event事件处理函数更新界面状态,事件对象在主线程中被处理,event函数在主线程中调用。(注意:发送的事件对象必须在堆空间创建,子线程创建时必须附带目标对象的地址信息)

    ​ 代码示例:

    1. 自定义事件类

      #ifndef PROGRESSEVENT_H
      #define PROGRESSEVENT_H
      
      #include <QEvent>
      
      class ProgressEvent : public QEvent
      {
      public:
          const static Type TYPE = static_cast<Type>(QEvent::User+0xFF);
          ProgressEvent(int progress = 0):QEvent(TYPE),m_progress(progress){
          }
          int progress() const
          {
              return m_progress;
          }
      private:
          int m_progress;
      };
      
      #endif // PROGRESSEVENT_H
      
      
    2. 工作线程类

      #ifndef WORKTHREAD_H
      #define WORKTHREAD_H
      
      #include <QThread>
      #include <QApplication>
      #include "progressevent.h"
      
      class WorkThread : public QThread
      {
          Q_OBJECT
      public:
          WorkThread():m_stop(false){
          }
          void run(){
              work();
              exec();
          }
          void stop(){
              m_stop = true;
          }
          void work(){
              for(int i = 0; i < 11; i++){
                  QApplication::postEvent(parent(), new ProgressEvent(i*10));
                  sleep(1);
              }
          }
      private:
          volatile bool m_stop;
      
      };
      
      #endif // WORKTHREAD_H
      
      
    3. 主界面类

    /*eventwidget.h*/
    #ifndef EVENTWIDGET_H
    #define EVENTWIDGET_H
    
    #include <QWidget>
    #include<QProgressBar>
    #include "progressevent.h"
    #include "workthread.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class EventWidget; }
    QT_END_NAMESPACE
    
    class EventWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        EventWidget(QWidget *parent = nullptr);
        ~EventWidget();
        bool event(QEvent* event);
        void closeEvent(QCloseEvent *event);
    
    private:
        Ui::EventWidget *ui;
        QProgressBar* m_progress;
        WorkThread* m_thread;
    };
    #endif // EVENTWIDGET_H
    
    /*eventwidget.cpp*/
    #include "eventwidget.h"
    #include "ui_eventwidget.h"
    EventWidget::EventWidget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::EventWidget)
    {
        ui->setupUi(this);
        m_progress = new QProgressBar(this);
        m_progress->move(10,10);
        m_progress->setMinimum(0);
        m_progress->setMaximum(100);
        m_progress->setTextVisible(true);
        m_progress->resize(100,30);
        m_thread = new WorkThread();
        m_thread->setParent(this);
        m_thread->start();
    }
    EventWidget::~EventWidget()
    {
        if(m_thread->isRunning()){
            m_thread->quit();
        }
        delete ui;
    }
    bool EventWidget::event(QEvent* event)
    {
        bool ret = true;
        if(event->type() == ProgressEvent::TYPE){
            ProgressEvent* evt = dynamic_cast<ProgressEvent*>(event);
            if(evt!=NULL){
                m_progress->setValue(evt->progress());
            }
        }
        else{
            ret = QWidget::event(event);
        }
        return ret;
    }
    void EventWidget::closeEvent(QCloseEvent *event)
    {
        m_thread->terminate();
    }
    
    
    更多相关内容
  • GUI线程 Qt应用程序exec后就会生成一个线程,这个线程就是主线程,在GUI程序中也称为GUI线程。主线程也是唯一允许创建QApplication或QCoreAppliation对象,比并且可以对创建的对象调用exec()的线程,从而进入事件...

    GUI线程

    Qt应用程序exec后就会生成一个线程,这个线程就是主线程,在GUI程序中也称为GUI线程。主线程也是唯一允许创建QApplication或QCoreAppliation对象,比并且可以对创建的对象调用exec()的线程,从而进入事件循环。

    在只有主线程即单线程的情况中,每一个事件的发生都需要进入事件循环进行等待,如有在某一步计算量比较大,则会一直占用CPU不放,导致其它操作无法完成,界面陷入冻结状态
    所以,对于计算量大的操作,需要放到一个单独的线程进行计算,然后通过信号槽的方式和GUI线程进行通信。

    QT多线程的实现方式

    1. 重写QThread的run()

    实现方法:
    新建一个类,继承QThread,重写虚函数run();

      class WorkerThread : public QThread
      {
          Q_OBJECT
          void run() override {
              QString result;
              /* ... here is the expensive or blocking operation ... */
              emit resultReady(result);
          }
      signals:
          void resultReady(const QString &s);
      };
     
      void MyObject::startWorkInAThread()
      {
          WorkerThread *workerThread = new WorkerThread(this);
          connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
          connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
          workerThread->start();
      }
    

    优点
    可以通过信号槽与外界通信
    缺点
    每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
    要自己进行内存的管理(线程的释放和删除);频繁的创建和释放会给系统带来比较大的开销。
    适用场景
    QThread适用于那些常驻内存的任务

    2. QThread的moveToThread**

    实现方法
    创建一个集成QObject的类(myObject),new 一个QThread,并调用moveToThread(),将创建和的myObject类移动到子线程中,子线程(myObject)通过发发送信号,利用信号槽机制,与主线程进行通信。

     
      class Worker : public QObject
      {
          Q_OBJECT
     
      public slots:
          void doWork(const QString &parameter) {
              QString result;
              /* ... here is the expensive or blocking operation ... */
              emit resultReady(result);
          }
     
      signals:
          void resultReady(const QString &result);
      };
     
      class Controller : public QObject
      {
          Q_OBJECT
          QThread workerThread;
      public:
          Controller() {
              Worker *worker = new Worker;
              worker->moveToThread(&workerThread);
              connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
              connect(this, &Controller::operate, worker, &Worker::doWork);
              connect(worker, &Worker::resultReady, this, &Controller::handleResults);
              workerThread.start();
          }
          ~Controller() {
              workerThread.quit();
              workerThread.wait();
          }
      public slots:
          void handleResults(const QString &);
      signals:
          void operate(const QString &);
      };
    

    优点
    1、相对重写QThread::run()函数的方法更加灵活:

    moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,movetothread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,movetothread给我们编写代码提供了新的思路,当然不是说子类化qthread不好,只是你应该知道还有这种方式去调用线程。

    轻量级的函数可以用movethread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得movetothread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveThread的好处就来了,你可以把这段代码的从属着movetothread,把代码移到槽函数,用信号触发它就行了。其它的话movetothread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而movetothread就有很多触发的入口。

    3. QRunnalble的run**

    Qrunnable是所有可执行对象的基类。我们可以继承Qrunnable,并重写虚函数
    实现方法
    1、继承QRunnable。和QThread使用一样, 首先需要将你的线程类继承于QRunnable。
    2、重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
    3、使用QThreadPool启动线程

    class Runnable:public QRunnable
    {
           //Q_OBJECT   注意了,Qrunnable不是QObject的子类。
    public:
           Runnable();
           ~Runnable();
           void run();
    };
     
     
    Runnable::Runnable():QRunnable()
    {
     
    }
     
    Runnable::~Runnable()
    {
           cout<<"~Runnable()"<<endl;
    }
     
    void Runnable::run()
    {
           cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl;
           cout<<"dosomething ...."<<endl;
    }
    int main(int argc, char *argv[])
    {
           QCoreApplication a(argc, argv);
           cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
           Runnable runObj;
           QThreadPool::globalInstance()->start(&runObj);
           returna.exec();
    }
    

    优点:
    无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
    缺点:
    不能使用信号槽与外界通信。
    适用场景:
    QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。
    和QThread的区别

    与外界通信方式不同。由于QThread是继承于QObject的,但QRunnable不是,所以在QThread线程中,可以直接将线程中执行的结果通过信号的方式发到主程序,而QRunnable线程不能用信号槽,只能通过别的方式,等下会介绍。

    启动线程方式不同。QThread线程可以直接调用start()函数启动,而QRunnable线程需要借助QThreadPool进行启动。

    资源管理不同。QThread线程对象需要手动去管理删除和释放,而QRunnable则会在QThreadPool调用完成后自动释放。

    Qt线程之QRunnable的使用详解

    4. QtConcurrent的run**

    Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。

    QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。详见前面的文章介绍,这里不再赘述。

    需要注意的是,由于该线程取自全局线程池QThreadPool,函数不能立马执行,需要等待线程可用时才会运行。
    实现方法
    1、首先在.pro文件中加上以下内容:QT += concurrent

    2、包含头文件#include ,然后就可以使用QtConcurrent了

    QFuture fut1 = QtConcurrent::run(func, QString(“Thread 1”)); fut1.waitForFinished();

    #include <QtCore/QCoreApplication>
    #include <QDebug>
    #include <QThread>
    #include <QString>
    #include <QtConcurrent/QtConcurrentRun>
    #include <QTime>
    #include<opencv2\opencv.hpp>
    #include"XxwImgOp.h"
    #ifdef _DEBUG
    #pragma comment(lib,".\\XxwImgOpd.lib")
    #else
    #pragma comment(lib,".\\XxwImgOp.lib")
    #endif // _DEBUG
     
    using namespace QtConcurrent;
     
    XxwImgOp xxwImgOp;
    cv::Mat src = cv::imread("1.bmp", 0);
    cv::Mat  dst, dst1, dst2;
     
    void hello(cv::Mat src)
    {
    	qDebug() << "-----------" << QTime::currentTime()<<"------------------------"<<QThread::currentThreadId();
    	xxwImgOp.fManualThreshold(src, dst, 50, 150);
    	qDebug() <<"************" << QTime::currentTime() <<"**********************"<< QThread::currentThreadId();
     
    }
     
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QFuture<void> f1 = run(hello,  src);
        QFuture<void> f2 = run(hello, src);
        //阻塞调用,阻塞主线程直到计算完成
        f1.waitForFinished();
        f2.waitForFinished();
     
        //阻塞为End的执行顺序
        qDebug() << "End";
        return a.exec();
    }
    

    特点:

    //调用外部函数 QFuture f1 =QtConcurrent::run(func,QString(“aaa”));

    //调用类成员函数 QFuture f2 =QtConcurrent::run(this,&MainWindow::myFunc,QString(“bbb”));

    要为其指定线程池,可以将线程池的指针作为第一个参数传递进去

    向该函数传递参数,需要传递的参数,则跟在函数名之后

    可以用run函数的返回值funIr来控制线程。
    如: funIr.waitForFinished(); 等待线程结束,实现阻塞。
    funIr.isFinished() 判断线程是否结束
    funIr, isRunning() 判断线程是否在运行
    funIr的类型必须和线程函数的返回值类型相同,可以通过
    funIr.result() 取出线程函数的返回值

    缺点
    不能直接用信号和槽函数来操作线程函数,eg : 当线程函数结束时,不会触发任何信号。

    多线程间的通信

    方法一

    将多线程类对象封装为GUI界面类的类成员
    然后在子线程定义信号函数,通过信号槽机制,向界面组件emit发射信号,从而实现间接操作.

    方法二

    使用QApplication::postEvent()实现向界面发送事件,从而能够封装一个自定义类

    方法三

    使用Invokes()函数来调用界面组件的信号槽

    一般使用该函数(用来调用对方的私有信号或槽):
    在这里插入图片描述
    该函数的连接方式默认使用的是Qt::AutoConnection
    表示如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
    比如,当我们想调用一个obj下的compute(QString, int, double)槽函数时:
    则只需要写入:

    QMetaObject::invokeMethod(obj, "compute",
                                Q_ARG(QString, "sqrt"),                        
                                Q_ARG(int, 42),
                                Q_ARG(double, 9.7));
    

    注意
    在QThread线程中不能直接创建QWidget之类的界面组件.
    因为在QT中,所有界面组件相关的操作都必须在主线程中(也就是GUI thread)
    所以, QThread线程不能直接操作界面组件.

    易犯错误

    1、子线程中操作UI

    Qt创建的子线程中是不能对UI对象进行任何操作的,即QWidget及其派生类对象,这个是我掉的第一个坑。可能是由于考虑到安全性的问题,所以Qt中子线程不能执行任何关于界面的处理,包括消息框的弹出。正确的操作应该是通过信号槽,将一些参数传递给主线程,让主线程(也就是Controller)去处理。

    2、信号的参数问题

    元对象系统即是提供了Qt类对象之间的信号槽机制的系统。要使用信号槽机制,类必须继承自QObject类,并在私有声明区域声明Q_OBJECT宏。当一个cpp文件中的类声明带有这个宏,就会有一个叫moc工具的玩意创建另一个以moc开头的cpp源文件(在debug目录下),其中包含了为每一个类生成的元对象代码。
    在使用connect函数的时候,我们一般会把最后一个参数忽略掉
    在这里插入图片描述
    我们一般会用到方式是有三种:

    * 自动连接(AutoConnection),默认的连接方式。如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。
    * 
    

    直接连接(DirectConnection)。当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行。
    *
    队列连接(QueuedConnection)。当控制权回到接受者所在线程的事件循环时,槽函数被调用。这时候需要将信号的参数塞到信号队列里。槽函数在接受者所在线程执行。

    signals:
        //自定义发送的信号
        void myThreadSignal(const int, string, string, string, string);
    

    貌似没什么问题,然而实际运行起来槽函数根本就没有被调用,程序没有崩溃,VS也没报错。在查阅了N多博客和资料中才发现,在线程间进行信号槽连接时,参数不能随便写。
    为什么呢?我的后四个参数是标准库中的string类型,这不是元对象系统内置的类型,也不是c++的基本类型,系统无法识别,然后就没有进入信号槽队列中了,自然就会出现问题。解决方法有三种,最简单的就是使用Qt的数据类型了

    第二种方法就是往元对象系统里注册这个类型。注意,在qRegisterMetaType函数被调用时,这个类型应该要确保已经被完好地定义了。

    qRegisterMetaType<MyClass>("MyClass");
    

    方法三是改变信号槽的连接方式,将默认的队列连接方式改为直接连接方式,这样的话信号的参数直接进入槽函数中被使用,槽函数立刻调用,不会进入信号槽队列中。但这种方式官方认为有风险,不建议使用。

    connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::DirectConnection)
    

    还有几点需要注意

    • 一定要用信号槽机制,别想着直接调用,你会发现并没有在子线程中执行。
    • 自定义的类不能指定父对象,因为moveToThread函数会将线程对象指定为自定义的类的父对象,当自定义的类对象已经有了父对象,就会报错。
    • 当一个变量需要在多个线程间进行访问时,最好加上voliate关键字,以免读取到的是旧的值。当然,Qt中提供了线程同步的支持,比如互斥锁之类的玩意,使用这些方式来访问变量会更加安全。
    展开全文
  • 文章目录 前奏 窗口代码 你能回答这些问题吗? 消息队列: 消息队列在何处呢?...通过KTHREAD.Win32Thread可以找到 并不是所有线程都要消息队列,只有GUI线程才有消息队列 一个GUI线程对应1个消息队列

    前奏

    首先我们来画一个窗口:
    在这里插入图片描述

    窗口代码

    #include<Windows.h>
    #include <stdio.h>
    #define _WIN32_WINNT 0x0500
    
    typedef struct _Color {
    
    	DWORD r;
    	DWORD g;
    	DWORD b;
    }Color;
    
    typedef struct _WindowClass {
    	DWORD x;
    	DWORD y;
    	DWORD width;
    	DWORD hight;
    	Color color;
    }WindowClass;
    
    void PaintWindwos(HDC hdc, WindowClass* p) {
    	HBRUSH hBrush;
    	hBrush = (HBRUSH)GetStockObject(DC_BRUSH);
    	
    	SelectObject(hdc, hBrush);//画刷
    	SetDCBrushColor(hdc, RGB(p->color.r, p->color.g, p->color.b));
    
    	MoveToEx(hdc, p->x, p->y, NULL);
    
    	LineTo(hdc, p->x + p->width, p->y);
    	LineTo(hdc, p->x + p->width, p->y + p->hight);
    	LineTo(hdc, p->x, p->y + p->hight);
    	LineTo(hdc, p->x, p->y);
    	Rectangle(hdc, p->x, p->y, p->x + p->width, p->y + p->width + 1);
    
    	DeleteObject(hBrush);
    }
    void main() {
    	char cMessage;   //消息
    	HWND  hwnd;		//画在哪
    	HDC  hdc;		//显卡缓存
    
    	//设置窗口参数:长宽高之类的
    	WindowClass wClass;
    	wClass.x = 0;
    	wClass.y = 0;
    	wClass.width = 800;
    	wClass.hight = 400;
    	wClass.color.r = 0xEF;
    	wClass.color.g = 0xEB;
    	wClass.color.b = 0xDE;
    
    	//画在哪
    
    	hwnd = GetDesktopWindow();
    	//hwnd=FindWindow("notepad.exe",NULL);
    
    	//获取DC设备句柄:可以把DC理解成显卡缓存
    	hdc = GetWindowDC(hwnd);
    	
    	cMessage = getchar();
    	for(;;) {
    		//画窗口
    		PaintWindwos(hdc, &wClass);
    	
    		//接收消息
    		
    		switch (cMessage)
    		{
    		case 'a':
    			wClass.color.r += 0x10;
    			wClass.color.g += 0x10;
    			wClass.color.b += 0x10;
    			break;
    		case 'b':
    			wClass.color.r += 0x20;
    			wClass.color.g += 0x20;
    			wClass.color.b += 0x20;
    			break;
    
    		default:
    			break;
    		}
    	}
    }
    

    在这里插入图片描述
    在这里呢我们可以通过控制台去控制窗口的颜色,那么接下来让我们一起来看看原理:

    你能回答这些问题吗?

    1. 什么是窗口句柄?在哪里?有什么用?
    2. 什么是消息?什么是消息队列?消息队列在哪 ?
    3. 什么是窗口过程?窗口过程是由谁调用的?没有消息循环窗口过程会执行吗?
    4. 为什么要有w32k.sys这个模块?
    5. 为什么只有使用图形界面的程序才可以访问KeServiceDescriptorTableShadow?
    6. 界面“卡死”的时候为什么鼠标还可以动?

    消息队列:

    在这里插入图片描述

    消息队列在何处呢?

    在这里插入图片描述

    首先我们假设把消息队列放在用户空间(3环),那么谁又来往用户空间的消息队列存储这些东西呢?
    最好的解决方案就是找一个专用进程来监听鼠标和键盘等,再来进行判断是属于哪个进程的消息队列,最后来进行消息分发(Linux解决方案)
    弊端:涉及了跨进程通信问题,专用进程传给其它进程。大量时间都花在跨进程。

    那么Windows如何解决的呢?

    首先普及一下:
    kernel32.dll ----------> ntoskrnl.exe(进程,线程,内存管
    user32.dll gdi32.dll -----------> win32k.sys(图形界面,消息管理)

    Windows已经画好的界面(windows提供的)GUI编程 user32.dll
    不用Windows提供的那些界面 GDI编程 gdi32.dll

    窗口句柄HWN

    针对窗口的句柄表,只有一个,(放在内核中)表是全局的,所有窗口共用的

    HDC   hdc;
    HPEN hpen;
    1.设备对象	画在哪
    hwnd    =(HWND)0x0003543;
    2.获取设备对象上下文
    hdc=GetDC(hwnd);
    3.创建画笔	设置线条属性
    hpen=CreatePen(PS_S0LID,5,RGB(0xFF,00,00));
    4.关联
    SelectObject(hdc,hpen);
    5.开始画
    LineTo(hdc,400,400);//gdi32.dll
    6.释放资源
    DeleteObject(hpen);
    ReleaseDC(hwnd,hdc);
    

    它把消息队列放在(内核空间)0环中,

    微软的解决方案:GUI线程
    GUI(自己使用微软提供的窗口函数,比如CreateWindow,CreateButton等创建的图形界面,这种API就叫做GUI)

    GDI(如果觉得微软提供的窗口不符合自己所需条件,需要自己画,所用的那些API就叫GDI)

    重点:

    1.当线程刚创建的时候,都是普通线程:
    Thread.ServiceTable -->KeServiceDescriptorTable(SSDT表)
    ServiceTable中存储了一张表,叫做系统服务表

    2.当线程第一次调用(与图形界面相关的模块 )Win32k.sys时(只要试图正常调用Win32k中的任何一个函数),会调用一个函数:PsConvertToGuiThread(把普通线程转换为GUI线程)

    PsConvertToGuiThread主要做几件事:
    a.扩充内核栈,必须换成64KB 的大内核栈,因为普通内核栈只有12KB大小。
    b.创建一个包含消息队列的结构体,并挂到KTHREAD上(也就是KTHREAD中的Win32Thread)
    c.Thread.ServiceTable–>KeServiceDescriptorShadow(SSDTShadow表)
    d.把需要的内存数据映射到本进程空间

    (SSDT表中只引用了一张表,只有一张系统服务表,也就是ntoskernel,win32k的第二张表它没有。但是SSDTShaow表中既包含了ntoskernel中的函数,又包含了Win32k(与图形界面相关的)中的函数)
    解释:
    如果是一个普通线程的话,那么ETHRED结构体中成员KTHREAD结构体中有一个Win32Thread成员它是为空(未使用图形界面相关的API)
    如果是一个GUI线程的话,那么ETHRED结构体中成员KTHREAD有一个Win32Thread成员它是一个地址值,指向一个结构体,指向一个_THREADINFO结构体,这个结构体里面又有一个成员,存放着消息队列
    在这里插入图片描述

    在这里插入图片描述

    总结:

    1. 消息队列存储在0环,通过KTHREAD.Win32Thread可以找到
    2. 并不是所有线程都要消息队列,只有GUI线程才有消息队列
    3. 一个GUI线程对应1个消息队列
    展开全文
  • java----GUI线程

    2011-08-29 17:31:06
    练习GUI编程和多线程,包括多线程的睡眠、唤醒,界面的简单编程,实现字幕滚动的功能
  • 在非gui线程使用QMessageBox

    千次阅读 2019-01-29 16:47:29
    Qt提供的MessageBox只可以在gui线程(主线程)使用,于是需要QMessageBox进行封装,可以在非gui线程内被调用。 特性: 1.可以在任何线程调用; 2.show后和默认的MessageBox一样是阻塞的,MessageBox关闭后才会...

    Qt提供的MessageBox只可以在gui线程(主线程)使用,于是需要QMessageBox进行封装,可以在非gui线程内被调用。
    特性:
    1.可以在任何线程调用;
    2.show后和默认的MessageBox一样是阻塞的,MessageBox关闭后才会返回。

    1. 使用moveToThread将QMessageBox实际调用移动到主线程中,并使用QEventLoop实现阻塞。

    声明

    #include <QMessageBox>
    #include <QEventLoop>
     
    class ThreadInformationMessageBox : public QObject
    {
        Q_OBJECT
     
    private:
        const QString m_strTitle;
        const QString m_strMessage;
     
    public:
        ThreadInformationMessageBox(const QString &strTitle, const QString &strMessage);
     
        static void show(const QString &strTitle, const QString &strMessage);
     
    private:
        void readyShow(void);
     
    private slots:
        void onShow(void);
    };
    

    实现

    ThreadInformationMessageBox::ThreadInformationMessageBox(const QString &strTitle, const QString &strMessage)
    	: m_strTitle(strTitle), 
    	  m_strMessage(strMessage)
    {
    }
     
    void ThreadInformationMessageBox::show(const QString &strTitle, const QString &strMessage)
    {
        QEventLoop eventLoop;
        auto messageBox = new ThreadInformationMessageBox(strTitle, strMessage);
        connect(messageBox, SIGNAL(destroyed()), &eventLoop, SLOT(quit()));
        messageBox->readyShow();
        eventLoop.exec();
    }
     
    void ThreadInformationMessageBox::readyShow(void)
    {
        this->moveToThread(qApp->thread());
        QTimer::singleShot(0, this, SLOT(onShow()));
    }
    
    void ThreadInformationMessageBox::onShow(void)
    {
        QMessageBox::information(NULL, m_strTitle, m_strMessage);
        this->deleteLater();
    }
    

    使用

    ThreadInformationMessageBox::show("Title", "Message");
    

    备注

    该QMessageBox没有指定的父窗体,位置可能不是期望的,如果需要指定位置,则需要进行相应设置或移动。
    
    1. 使用线程内阻塞的信号槽连接方式实现QMessageBox。

    代码

    Q_SIGNALS:
        int SendMessageSignal(const QString& title, const QString& content, QMessageBox::Icon icon, 
    		const QString& button0Text,
    		const QString& button1Text = QString(),
    		const QString& button2Text = QString());
    
    // 连接线程内的信号与匿名函数
    connect(pObjectHasThread, &ObjectHasThread::SendMessageSignal, this, [=](
    		const QString& title, const QString& content, QMessageBox::Icon icon, 
    		const QString& button0Text,
    		const QString& button1Text = QString(),
    		const QString& button2Text = QString()) -> int
    	{
    		switch (icon)
    		{
    		case QMessageBox::Information:
    			return QMessageBox::information(this, title, content, button0Text, button1Text, button2Text); break;
    		case QMessageBox::Warning:
    			return QMessageBox::warning(this, title, content, button0Text, button1Text, button2Text); break;
    		case QMessageBox::Question:
    			return QMessageBox::question(this, title, content, button0Text, button1Text, button2Text); break;
    		case QMessageBox::Critical:
    			return QMessageBox::critical(this, title, content, button0Text, button1Text, button2Text); break;
    		default: 
                return -1;
    		}
    	}, Qt::BlockingQueuedConnection);
    

    备注

    1. pObjectHasThread为自定义线程对象或含有线程操作的对象,需要在此线程操作中使用QMessageBox;
    2. 使用时直接在pObjectHasThread对象中发射SendMessageSignal即可;
    3. Qt::BlockingQueuedConnection连接方式实现了线程内阻塞,该种连接方式仅适用于信号和槽函数在不同线程中的情形;
    4. int为匿名函数的返回值,即用户在QMessageBox弹出框中的选择。
    
    展开全文
  • 【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 一般来说,编写gui客户端之外,都要编写几个work thread。... 增加几个work thread本身没有问题,但是gui和工作线程沟通
  • 当一个Qt应用程序开始执行时,只有一个线程在运行——初始线程,这是唯...如果我们使用多线程通信,像互斥锁的话,是会因为线程阻塞将GUI线程冻结。所以,互斥锁之类到的做法不支持线程与GUI线程通信。那么,解决...
  • GUI线程的异步并行设计

    千次阅读 2013-03-09 17:24:54
    话说,GUI的异步处理确实是个难题。真实环境的异步除了不阻塞主线程刷新,更重要的是界面上常常有很多互斥的操作,需要对线程做更为细致的控制。 原文连接 ... ...GUI线程的异步并行设计 ...GUI线程是采用异步并行设计
  • 浏览器常驻三大线程: js引擎线程GUI渲染线程,浏览器事件触发线程
  • 图形界面,大多会采用多线程来更新一些耗时的程序。以达到软件的流畅性使用。多线程的实现主要有以下几种方法:【timer】1、通过timer.timeout() 方法来实现在timeout后调用对应的触发函数:下面我们简单编写一个小...
  • 而Platform.runLater则从后台线程中执行JavaFX Application线程中的事情.您必须同步它们,其中一种方法是使用CountDownLatch类.这是一个例子:Service service = new Service() {@Overrideprotected Task createTask...
  • Android---手动创建线程与GUI线程同步的第三种方法: 在GUI线程中调用runOnUiThread(Runnable runnable), 此时work Thread与GUI线程在同一个线程中, 所以,可以直接在work thread中更改GUI中的UI(此方法比较简单...
  • Qt:在非gui线程使用QMessageBox

    千次阅读 2015-01-22 15:44:16
    Qt:在非gui线程使用QMessageBox
  • 3、从5.4开始,有了另一种新方法:Qtimer::singleShot(0),这玩意儿可不是定时启动那么简单,你给他0的时间他就是GUI线程安全的,完整例子可以测试: 1 2 3 4 5 6 7 8 9 ...
  • setTimeout解决GUI渲染线程阻塞问题

    千次阅读 2017-08-27 19:04:22
    我还记得我第一次用这个函数的时候,我天真的以为它就是js实现多线程的工具.当时用它实现了一个坦克大战的小游戏,玩儿不亦乐乎.可是随着在前端这条路上越走越远,对它理解开始产生了变化.它似乎开始蒙上了面纱,时常有...
  • Win32说明文件一再强调线程分为GUI线程和worker线程两种。GUI线程负责建造窗口以及处理主消息循环。Worker负责执行纯粹的运算工作,如重新计算或重新编页等,这些运算工作会导致主线程的消息队列失去反应。一般而言...
  • Java用多线程GUI实现购买车票界面
  • 该工程中使用到了PyQt5的GUI,多线程技术等。使用pycharm,在程序运行时,程序偶尔会莫名奇妙的退出,并只是报下面的错误: Process finished with exit code -1073741819 (0xC0000005) 纳尼,只是给出了这一个报错...
  • Qt GUI程序中单线程和多线程的区别

    千次阅读 2018-09-21 10:31:55
    转载:https://blog.csdn.net/hulifangjiayou/article/details/50170739 ... Qt应用程序exec后就会生成一个线程,这个线程就是主线程,在GUI程序中也称为GUI线程。主线程也是唯一允许创建QApplication或Q...
  • 上文中讲述了工作者线程将数据同步到GUI中的第一种方式,本文讲述第二种方式: Control.Invoke()与Control.BeginInvoke() // 摘要: // 在拥有此控件的基础窗口句柄的线程上执行指定的委托。 // // 参数: // ...
  • GUI中经常需要生成一个异步操作,使得GUI线程不至于堵塞并停止响应用户输入(鼠标、键盘等),然而异步完成后,完成该异步操作的的线程无法将结果更新至UI。因此,线程池线程必须采用某种方式让GUI线程更显UI。 有...
  • 本文介绍在使用PyQt5写多线程的界面时,如何在非主线程中实现在界面上弹窗(QMessageBox) 方法:在非主线程中发射槽信号,在主线程进行回应实现,具体方法为: Step 1: 创建界面文件(demo.py) from PyQt5 ...
  • 【写在前面】
  • java课程设计模拟交通灯。
  • import tkinter import threading import datetime import time root=tkinter.Tk() root...19] time.sleep(1) thread=threading.Thread(target=showTime) thread.daemon=True #守护线程 thread.start() root.mainloop()
  • JAVA利用多线程和Socket实现的在线聊天室,利用GUI制作用户界面,服务器端:可踢人,管理员私聊,发布系统消息,群聊,客户端:私聊,屏蔽,群聊。
  • 直接进入主题,据我所知,方法...2、使用QmetaMethod::invoke,这是可以直接在子线程调用的,看起来比较另类 3、从5.4开始,有了另一种新方法: https://labisart.com/blog/index.php/Home/Index/article/aid/225 ...
  • Qt 负责文件的读写 qml负责显示进度,文件读写在子线程中进行。代码可以直接运行。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 91,866
精华内容 36,746
关键字:

gui线程