Qt多线程

来源:互联网 发布:出售欧美邮箱数据 编辑:程序博客网 时间:2024/06/04 00:36

Qt多线程


1.QThread类

           QThread类并不是代表一个新的线程,而是QT提供的一个接口,用于控制一个子线程。每个QThread的实例就代表着对一个新线程的一个控制类。对于第一次使用QT多线程的人,或许就会很迷惑很不适应。

        QThread提供一个公共槽接口--start(),当你有一个QThread的实例例如

QThread q_thread

        当你调用q_thread.start()时,q_thread控制的子线程就会开始执行,执行的入口就是QThread类的virtual protected 函数 QThread::run(),此时,run()里面的语句都是在新起的一个线程里执行,默认QT自带的run()实现就是QThread::exec(),表示开始QThread类的消息循环。exec()执行后,q_thread就能接受到信号(如果有连接信号的话),执行响应的槽。

       重点来了,run()里面的语句是在新起的线程执行的,而q_thread收到信号,执行相应槽里面的语句,却是由QThread所在的线程执行的。包括继承QThread类的子类以及其信号槽。  
例如

一个QThread的子类:

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
protected:
    virtual void run ();

public slots:
    void MyThreadSlot();
signals:
    void MyThreadSignal();
};

void MyThread::run()
{
    int i = 0;
    i++;
    exec();
}


一个主对话框类,里面声明一个MyThread变量

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);

private:
    MyThread q_myThread;
public slots:
    void MainWindowSlot();

signals:
    void MainWindowSignal();
};


       当我们在MainWindow里调用q_myThread.start()时, MyThread::run()开始执行,假设主线程是Thread1,新起的线程是Thread2,

int i = 0;i++;都是在Thread2里执行。接着exec()后,MyThread的事件循环开始了。


       我们先

       connect(this,SIGNAL(MainWindowSigna()),&q_myThread,SLOT(MyThreadSlot()),Qt::QueuedConnection);


       当MainWindow里 emit MainWindowSignal()时,MyThread的MyThreadSlot()会执行,你会发现,MyThreadSlot()执行的线程并不是我们想的Thread2,而是Thread1,因为q_thread在MainWindow中,MainWindow就在主线程Thread1中。那么我们如何才能在让我们写的代码在Thread2执行呢,其中一个方法便是改写run(),函数。


void MyThread::run()
{
    while(1)
    {
        do_something();
    }
}

       当我们调用q_myThread.start()时,do_something()就会循环执行,并且在新的线程Thread2中。而MainWindow的消息循环继续在Thread1里执行。总算有点像Windows的API CreateThread()了,但是如何给他传参数,在MyThread()的参数表里加?可以,那么如何与Thread1同步?在参数里加个用于同步的锁的地址?这样两个类之间的耦合度又大大增加,MyThread类几乎是不可重用的了。以前在Windows写多线程,最多就写个函数和它的参数的结构体,现在在QT还得子类化QThread,而且得到一个结构奇差的多线程结构。可真麻烦。

        既然使用了QT,当然好好利用QT的信号槽机制,使得各类设计上比较独立,提高可重用性。一个更好的方法是利用QObject::moveToThread(QThread*)函数。前面我们说过,QThread的槽函数会在QThread类所在的线程执行,所以我们不应该子类化QThread,因为子类化之后它的槽函数依然会在MainWindow所在线程执行,往往不是我们所想要的。除非你想进入改写run()函数这个恶梦。

        我们声明一个新的类,继承QObject,因为QOject可以使用信号槽。更重要的因为moveToThread()就是QObject的成员函数。

        class MyObject : public QObject
        {
            Q_OBJECT
        public:
            MyObject();
        public slots:
            void MyObjectSlot();
        signals:
            void MyObjectSignal();
        };

        在MainWindow类中声明 MyObject q_myObj;
        在构造函数里做如下处理
        connect(this,SIGNAL(MainWindowSignal()),&q_myObj,SLOT(MyObjectSlot()),Qt::QueuedConnection);
        q_myObj.moveToThread(&q_myThread);
        q_myThread.start();

        当我们发送一个信号MainWindowSignal()信号时,发现MyObjectSlot()是在新线程Thread2里执行的,不妨在MyObjectSlot()里sleep1分钟,MainWindow还是能响应的。

        至于MyObject的设计就随个人喜好了。可以把需要用到新线程的工作封装到MyObject的槽函数里,并且可以通过信号来通知MyObject开始或停止工作,而且还可以传参数。设计上比重载run()要好得多。


        总结,要使用多线程,先继承QObject,在里面实现需要多线程运行的语句。然后在主线程里声明一个QThread对象,把自己实现的Obj moveToThread移动到新线程里, QThread::start()开始运行。
0 0
原创粉丝点击