首頁 > 軟體

C/C++ Qt QThread執行緒元件的具體使用

2021-11-25 19:01:14

QThread庫是QT中提供的跨平臺多執行緒實現方案,使用時需要繼承QThread這個基礎類別,並重寫實現內部的Run方法,由於該庫是基本庫,預設依賴於QtCore.dll這個基礎模組,在使用時無需引入其他模組.

實現簡單多執行緒

QThread庫提供了跨平臺的多執行緒管理方案,通常一個QThread物件管理一個執行緒,在使用是需要從QThread類繼承並重寫內部的Run方法,並在Run方法內部實現多執行緒程式碼.

#include <QCoreApplication>
#include <iostream>
#include <QThread>

class MyThread: public QThread
{

protected:
    volatile bool m_to_stop;

protected:
    // 執行緒函數必須使用Run作為開始
    void run()
    {
        for(int x=0; !m_to_stop && (x <10); x++)
        {
            msleep(1000);
            std::cout << objectName().toStdString() << std::endl;
        }
    }

public:
    MyThread()
    {
        m_to_stop = false;
    }

    // 用於設定結束符號為真
    void stop()
    {
        m_to_stop = true;
    }

    // 輸出執行緒執行狀態
    void is_run()
    {
        std::cout << "Thread Running = " << isRunning() << std::endl;
    }

    // 輸出執行緒完成狀態(是否結束)
    void is_finish()
    {
        std::cout << "Thread Finished = " << isFinished() << std::endl;
    }

};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 定義執行緒陣列
    MyThread thread[10];

    // 設定執行緒物件名字
    for(int x=0;x<10;x++)
    {
        thread[x].setObjectName(QString("thread => %1").arg(x));
    }

    // 批次呼叫run執行
    for(int x=0;x<10;x++)
    {
        thread[x].start();
        thread[x].is_run();
        thread[x].isFinished();
    }

    // 批次呼叫stop關閉
    for(int x=0;x<10;x++)
    {
        thread[x].wait();
        thread[x].stop();

        thread[x].is_run();
        thread[x].is_finish();
    }

    return a.exec();
}

向執行緒中傳遞引數

執行緒在執行前可以通過呼叫MyThread中的自定義函數,並在函數內實現引數賦值,實現執行緒傳參操作.

#include <QCoreApplication>
#include <iostream>
#include <QThread>

class MyThread: public QThread
{
protected:
    int m_begin;
    int m_end;
    int m_result;

    void run()
    {
        m_result = m_begin + m_end;
    }

public:
    MyThread()
    {
        m_begin = 0;
        m_end = 0;
        m_result = 0;
    }

    // 設定引數給當前執行緒
    void set_value(int x,int y)
    {
        m_begin = x;
        m_end = y;
    }

    // 獲取當前執行緒名
    void get_object_name()
    {
        std::cout << "this thread name => " << objectName().toStdString() << std::endl;
    }

    // 獲取執行緒返回結果
    int result()
    {
        return m_result;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyThread thread[3];

    // 分別將不同的引數傳入到執行緒函數內
    for(int x=0; x<3; x++)
    {
        thread[x].set_value(1,2);
        thread[x].setObjectName(QString("thread -> %1").arg(x));
        thread[x].start();
    }

    // 等待所有執行緒執行結束
    for(int x=0; x<3; x++)
    {
        thread[x].get_object_name();
        thread[x].wait();
    }

    // 獲取執行緒返回值並相加
    int result = thread[0].result() + thread[1].result() + thread[2].result();
    std::cout << "sum => " << result << std::endl;

    return a.exec();
}

QMutex 互斥同步執行緒鎖

QMutex類是基於互斥量的執行緒同步鎖,該鎖lock()鎖定與unlock()解鎖必須配對使用,執行緒鎖保證執行緒間的互斥,利用執行緒鎖能夠保證臨界資源的安全性.

  • 執行緒鎖解決的問題: 多個執行緒同時操作同一個全域性變數,為了防止資源的無序覆蓋現象,從而需要增加鎖,來實現多執行緒搶佔資源時可以有序執行.
  • 臨界資源(Critical Resource): 每次只允許一個執行緒進行存取 (讀/寫)的資源.
  • 執行緒間的互斥(競爭): 多個執行緒在同一時刻都需要存取臨界資源.
  • 一般性原則: 每一個臨界資源都需要一個執行緒鎖進行保護.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QMutex>

static QMutex g_mutex;      // 執行緒鎖
static QString g_store;     // 定義全域性變數

class Producer : public QThread
{
protected:
    void run()
    {
        int count = 0;

        while(true)
        {
            // 加鎖
            g_mutex.lock();

            g_store.append(QString::number((count++) % 10));
            std::cout << "Producer -> "<< g_store.toStdString() << std::endl;

            // 釋放鎖
            g_mutex.unlock();
            msleep(900);
        }
    }
};

class Customer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            g_mutex.lock();
            if( g_store != "" )
            {
                g_store.remove(0, 1);
                std::cout << "Curstomer -> "<< g_store.toStdString() << std::endl;
            }

            g_mutex.unlock();
            msleep(1000);
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer p;
    Customer c;

    p.setObjectName("producer");
    c.setObjectName("curstomer");

    p.start();
    c.start();

    return a.exec();
}

QMutexLocker是在QMutex基礎上簡化版的執行緒鎖,QMutexLocker會保護加鎖區域,並自動實現互斥量的鎖定和解鎖操作,可以將其理解為是智慧版的QMutex鎖,該鎖只需要在上方程式碼中稍加修改即可.

#include <QMutex>
#include <QMutexLocker>

static QMutex g_mutex;      // 執行緒鎖
static QString g_store;     // 定義全域性變數

class Producer : public QThread
{
protected:
    void run()
    {
        int count = 0;

        while(true)
        {
			// 增加智慧執行緒鎖
            QMutexLocker Locker(&g_mutex);

            g_store.append(QString::number((count++) % 10));
            std::cout << "Producer -> "<< g_store.toStdString() << std::endl;

            msleep(900);
        }
    }
};

互斥鎖存在一個問題,每次只能有一個執行緒獲得互斥量的許可權,如果在程式中有多個執行緒來同時讀取某個變數,那麼使用互斥量必須排隊,效率上會大打折扣,基於QReadWriteLock讀寫模式進行程式碼段鎖定,即可解決互斥鎖存在的問題.

QReadWriteLock 讀寫同步執行緒鎖

該鎖允許使用者以同步讀lockForRead()或同步寫lockForWrite()兩種方式實現保護資源,但只要有一個執行緒在以寫的方式操作資源,其他執行緒也會等待寫入操作結束後才可繼續讀資源.

#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QMutex>
#include <QReadWriteLock>

static QReadWriteLock g_mutex;      // 執行緒鎖
static QString g_store;             // 定義全域性變數

class Producer : public QThread
{
protected:
    void run()
    {
        int count = 0;

        while(true)
        {
            // 以寫入方式鎖定資源
            g_mutex.lockForWrite();

            g_store.append(QString::number((count++) % 10));

            // 寫入後解鎖資源
            g_mutex.unlock();

            msleep(900);
        }
    }
};

class Customer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            // 以讀取方式寫入資源
            g_mutex.lockForRead();
            if( g_store != "" )
            {
                std::cout << "Curstomer -> "<< g_store.toStdString() << std::endl;
            }

            // 讀取到後解鎖資源
            g_mutex.unlock();
            msleep(1000);
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer p1,p2;
    Customer c1,c2;

    p1.setObjectName("producer 1");
    p2.setObjectName("producer 2");

    c1.setObjectName("curstomer 1");
    c2.setObjectName("curstomer 2");

    p1.start();
    p2.start();

    c1.start();
    c2.start();

    return a.exec();
}

QSemaphore 基於訊號執行緒鎖

號誌是特殊的執行緒鎖,號誌允許N個執行緒同時存取臨界資源,通過acquire()獲取到指定資源,release()釋放指定資源.

#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QSemaphore>

const int SIZE = 5;
unsigned char g_buff[SIZE] = {0};

QSemaphore g_sem_free(SIZE); // 5個可生產資源
QSemaphore g_sem_used(0);    // 0個可消費資源

// 生產者生產產品
class Producer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            int value = qrand() % 256;

            // 若無法獲得可生產資源,阻塞在這裡
            g_sem_free.acquire();

            for(int i=0; i<SIZE; i++)
            {
                if( !g_buff[i] )
                {
                    g_buff[i] = value;
                    std::cout << objectName().toStdString() << " --> " << value << std::endl;
                    break;
                }
            }

            // 可消費資源數+1
            g_sem_used.release();

            sleep(2);
        }
    }
};

// 消費者消費產品
class Customer : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            // 若無法獲得可消費資源,阻塞在這裡
            g_sem_used.acquire();

            for(int i=0; i<SIZE; i++)
            {
                if( g_buff[i] )
                {
                    int value = g_buff[i];

                    g_buff[i] = 0;
                    std::cout << objectName().toStdString() << " --> " << value << std::endl;
                    break;
                }
            }

            // 可生產資源數+1
            g_sem_free.release();

            sleep(1);
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer p1;
    Customer c1;

    p1.setObjectName("producer");
    c1.setObjectName("curstomer");

    p1.start();
    c1.start();

    return a.exec();
}

到此這篇關於C/C++ Qt QThread執行緒元件的具體使用的文章就介紹到這了,更多相關Qt QThread執行緒使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com