【零基础学QT】【038】Qt多线程框架和C++11多线程框架

使用QThread实现多线程


	#include "qlib.h"
	
	class PrintThread : public QThread {
	  private:
	    //通过bool来控制运行条件
	    bool running = true;
	
	  public:
	    PrintThread() {}
	    //在析构函数中自动结束线程
	    //很重要,可以在主线程结束时,保证子线程正常结束
	    ~PrintThread() {
	        running = false;
	        this->requestInterruption();
	        this->wait();
	    }
	
	  protected:
	    //重写run方法,指定线程工作
	    void run() {
	        while (running) {
	            wcout << L"线程1正在运行" << endl;
	            msleep(500);
	        }
	    }
	};
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    //创建并开始线程
	    PrintThread t1;
	    t1.start();
	
	    return app.exec();
	}

使用lambda创建QThread
QThread有一个很明显的缺点就是,工作任务都写在run方法里面,如果工作任务不同,就要重新定义一个QThread类
我们可以将run方法抽取出来,通过函数指针传入一个方法来指定工作任务,再通过lambda来简化定义方法的代码


	#include "qlib.h"
	
	class PrintThread : public QThread {
	  private:
	    bool running = true;
	    //通过函数指针来指定run方法的工作
	    void (*pFunc)(PrintThread* t) = nullptr;
	
	  public:
	    PrintThread(void (*pFunc)(PrintThread* t)) : QThread() {
	        this->pFunc = pFunc;
	    }
	
	    ~PrintThread() {
	        running = false;
	        this->requestInterruption();
	        this->wait();
	        pFunc = nullptr;
	    }
	
	    bool isRunning() {
	        return running;
	    }
	
	  protected:
	    void run() {
	        //调用函数指针来处理工作
	        while (running) pFunc(this);
	    }
	};
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    //通过lambda来创建线程,这样就可以让不同线程处理不同的工作
	    PrintThread* t1 = new PrintThread([](PrintThread* t) -> void {
	        while (t->isRunning()) {
	            wcout << L"线程1正在运行" << endl;
	            t->msleep(500);
	        }
	    });
	    t1->start();
	
	    //再创建一个线程
	    PrintThread* t2 = new PrintThread([](PrintThread* t) -> void {
	        while (t->isRunning()) {
	            wcout << L"线程2正在运行" << endl;
	            t->msleep(500);
	        }
	    });
	    t2->start();
	
	    return app.exec();
	}

使用QtConcurrent实现多线程


	#include "qlib.h"
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    //开启一个线程执行方法
	    //异步执行,执行完后通过QFuture.value可以获得结果
	    QFuture<int> future1 = QtConcurrent::run(
	        [](int a, int b) -> int {
	            for (int i = 0; i < 100000; i++) wcout << L"线程1正在运行" << endl;
	            return 1;
	        },
	        1, 2);
	
	    //再开启一个线程执行方法
	    QFuture<int> future2 = QtConcurrent::run([]() -> int {
	        for (int i = 0; i < 100000; i++) wcout << L"线程2正在运行" << endl;
	        return 1;
	    });
	
	    //执行带参数的方法
	    auto pFunc = [](int count) -> int {
	        for (int i = 0; i < count; i++) wcout << L"线程3正在运行" << endl;
	        return 1;
	    };
	    QFuture<int> future3 = QtConcurrent::run(pFunc, 100000);
	
	    //等待执行完毕,获取结果
	    //这个操作会阻塞主线程
	    //如果不想阻塞主线程,可以不使用QFuture,而是在子线程中通过信号槽来通知主线程
	    future1.waitForFinished();
	    future2.waitForFinished();
	    future3.waitForFinished();
	
	    return app.exec();
	}

在线程池中执行QtConcurrent


	#include "qlib.h"
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    //创建一个函数指针,指向匿名lambda方法
	    auto pFunc = [](string message) -> int {
	        for (int i = 0; i < 10; i++) {
	            cout << message << endl;
	            QThread::msleep(500);
	        }
	        return 1;
	    };
	
	    //创建一个线程池
	    //如果线程池中有足够线程,则所有任务并发执行
	    //如果线程池中只有一个线程,则所有任务轮流执行
	    //单例线程池经过适当封装,就可以作为一个任务队列使用
	    QThreadPool threadPool;
	    threadPool.setMaxThreadCount(1);
	
	    //向线程池中添加任务,并立刻执行
	    string param1 = "Thread-1";
	    string param2 = "Thread-2";
	    string param3 = "Thread-3";
	    QFuture<int> future1 = QtConcurrent::run(&threadPool, pFunc, param1);
	    QFuture<int> future2 = QtConcurrent::run(&threadPool, pFunc, param2);
	    QFuture<int> future3 = QtConcurrent::run(&threadPool, pFunc, param3);
	
	    return app.exec();
	}

利用C++原生代码实现多线程


	#include "qlib.h"
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    //创建一个函数指针,指向匿名lambda方法
	    auto pFunc = [](string message) {
	        for (int i = 0; i < 100; i++) {
	            cout << message << endl;
	            QThread::msleep(500);
	        }
	    };
	
	    //创建线程
	    std::thread t1(pFunc, "Thread-A is Running");
	    std::thread t2(pFunc, "Thread-B is Running");
	    std::thread t3(pFunc, "Thread-C is Running");
	
	    return app.exec();
	}

C++原生代码中止线程


	#include "qlib.h"
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    auto pFunc = [](bool* running) {
	        while (*running) {
	            cout << "Thread-1 is Running" << endl;
	            QThread::msleep(1000);
	        }
	    };
	
	    //创建一个线程打印
	    bool* running = new bool(true);
	    std::thread t1(pFunc, running);
	
	    //创建一个定时线程,10秒后停止打印线程
	    std::thread t2([running] {
	        QThread::msleep(10000);
	        *running = false;
	        cout << "Stop Thread-1" << endl;
	    });
	
	    return app.exec();
	}

通过QMutex控制线程同步


	#include "qlib.h"
	
	int main(int argc, char* argv[]) {
	    QApplication app(argc, argv);
	
	    QMainWindow w;
	    w.show();
	
	    //设置字符编码
	    setlocale(LC_ALL, ".ACP");
	
	    QMutex* mutex = new QMutex();
	
	    QtConcurrent::run([mutex]() {
	        mutex->lock();
	        wcout << L"线程1获得同步锁" << endl;
	        QThread::msleep(5000);
	        wcout << L"线程1释放同步锁" << endl;
	        mutex->unlock();
	    });
	
	    QtConcurrent::run([mutex]() {
	        QThread::msleep(1000);
	        wcout << L"线程2等待同步锁" << endl;
	        mutex->lock();
	        wcout << L"线程2获得同步锁" << endl;
	        mutex->unlock();
	    });
	
	    return app.exec();
	}

简化的QMutex:QMutexLocker
QMutexLocker是对QMutex的封装简化,它持有一个QMutex的引用
它会在创建时通过构造函数自动调用QMutex.lock方法,在作用域结束时,通过析构函数自动调用QMutex.unlock方法


    QMutex mutex;

    QtConcurrent::run([&mutex]() {
        QMutexLocker locker(&mutex);
        wcout << L"线程1获得同步锁" << endl;
        QThread::msleep(5000);
        wcout << L"线程1释放同步锁" << endl;
    });

    QtConcurrent::run([&mutex]() {
        QThread::msleep(1000);
        wcout << L"线程2等待同步锁" << endl;
        QMutexLocker locker(&mutex);
        wcout << L"线程2获得同步锁" << endl;
    });

QReadWriteLock
QReadWriteLock中的Read/Write,并不代表实际操作是读还是写,仅仅为了方便使用,是提供了两种不同类型的锁
一个线程通过lockForWrite获取了锁,其它线程调用lockForWrite就得等待,调用lockForRead则无需等待
一个线程通过lockForRead获取了锁,其它线程调用lockForRead就得等待,调用lockForWrite则无需等待
至于执行了lockForWrite之后,是不是真的进行了写操作,对Lock是毫无影响的
当然,我们尽量让实际操作和QReadWriteLock的逻辑意义一致,不要去破坏接口本身的设计意图


    QReadWriteLock lock;

    QtConcurrent::run([&lock]() {
        wcout << L"线程1获得同步锁" << endl;
        lock.lockForWrite();
        wcout << L"线程1写入数据" << endl;
        QThread::msleep(5000);
        wcout << L"线程1释放同步锁" << endl;
        lock.unlock();
    });

    QtConcurrent::run([&lock]() {
        QThread::msleep(1000);
        wcout << L"线程2等待同步锁" << endl;
        lock.lockForWrite();
        wcout << L"线程2写入数据" << endl;
        wcout << L"线程2释放同步锁" << endl;
        lock.unlock();
    });

简化的QReadWriteLock:QReadLock,QWriteLock
QReadLock,QWriteLock和QMutexLocker的原理用法都是一致的
即持有一个QReadWriteLock引用,自动加锁解锁


    QReadWriteLock lock;

    QtConcurrent::run([&lock]() {
        wcout << L"线程1获得同步锁" << endl;
        QWriteLocker locker(&lock);
        wcout << L"线程1写入数据" << endl;
        QThread::msleep(5000);
        wcout << L"线程1释放同步锁" << endl;
    });

    QtConcurrent::run([&lock]() {
        QThread::msleep(1000);
        wcout << L"线程2等待同步锁" << endl;
        QWriteLocker locker(&lock);
        wcout << L"线程2写入数据" << endl;
        wcout << L"线程2释放同步锁" << endl;
    });

QSemaphore
QSemaphore在QMutex的基础上,增加了一个并发量功能,可允许若干个线程同时访问
当QSemaphore并发量达到指定上限,信号量用完时,申请信号量的线程就需要等待


    QSemaphore semaphore(3);

    QtConcurrent::run([&semaphore]() {
        semaphore.acquire(3);
        wcout << L"线程1获得三个信号量" << endl;
        QThread::msleep(5000);
        wcout << L"线程1释放一个信号量" << endl;
        semaphore.release(1);
    });

    QtConcurrent::run([&semaphore]() {
        QThread::msleep(1000);
        wcout << L"线程2请求一个信号量" << endl;
        semaphore.acquire(1);
        wcout << L"线程2获得一个信号量" << endl;
        wcout << L"线程2请求一个信号量" << endl;
        semaphore.acquire(1);  //由于线程1只释放了一个信号量,这里会无限等待
        wcout << L"线程2获得一个信号量" << endl;
    });

QWaitCondition
QWaitCondition可以允许线程在条件不符合时,进入等待状态,等到条件成立时,再继续执行
QWaitCondition需要配合QMutex使用,以保证在多线程情景下,只有一个线程会修改条件值,从而保证条件值的准确性


    //这里以生产者消费者案例,来模拟QWaitCondition的应用场景
    //生产者生产物品加入队列,消费者从队列中取出物品消费
    //仓库已满,则生产者不再生产,队列已空,则消费者不再消费

    const long produceInterval = 500;  //生产间隔
    const long consumeInterval = 1000;  //消费间隔
    const int storeSize = 1;  //仓库容量,达到仓库上限后,不再生产
    QQueue<string> productQueue;  //产品队列,队列为空时,不再消费

    QMutex mutex;  //用来保证队列在并发时的同步性
    QWaitCondition storeIsFull;  //根据仓库是否已满,来等待或通知
    QWaitCondition storeIsEmpty;  //根据仓库是否已空,来等待或通知

	//生产者线程
    QtConcurrent::run([&productQueue, &mutex, &storeIsFull, &storeIsEmpty]() {
        while (true) {
            mutex.lock();
            //如果仓库已满,则不再生产,进入等待状态,并交出productQueue的使用权
            if (productQueue.size() == storeSize) storeIsFull.wait(&mutex);
            //收到其它线程的通知,仓库已经有空位,继续生产,重新获得productQueue的使用权
            string productName = QUuid::createUuid().toString().toStdString();
            productQueue.enqueue(productName);
            storeIsEmpty.wakeOne();
            cout << "Produce Product:" << productName << endl;
            mutex.unlock();
            QThread::msleep(produceInterval);
        }
    });

	//消费者线程
    QtConcurrent::run([&productQueue, &mutex, &storeIsFull, &storeIsEmpty]() {
        while (true) {
            mutex.lock();
            //如果仓库已空,则不再消费,并交出productQueue的使用权
            if (productQueue.isEmpty()) storeIsEmpty.wait(&mutex);
            //收到其它线程的通知,仓库已经有货物,继续消费,重新获得productQueue的使用权
            string productName = productQueue.dequeue();
            cout << "Consume Product:" << productName << endl;
            storeIsFull.wakeOne();
            mutex.unlock();
            QThread::msleep(consumeInterval);
        }
    });

QWaitCondition和QMutex的区别
其实通过以上讲解,大家已经能够自己总结出两者的区别,为表郑重,特地总结下

  • QMutex是占有式的,一个线程lock后,其它线程都无法lock
  • QWaitCondition是通知式的,条件不符合,当前线程就进入wait状态,其它线程可以通过wake打破当前线程的wait状态
  • 一个用来竞争资源使用权,一个用来通知其它线程条件变更,是两种不同的功能
  • QWaitCondition需要配合QMutex,因为如果没有QMutex去保护条件的话,wake之后,条件值就可能被其它线程修改

C++标准库线程同步接口
C++标准库也提供了mutex,lock,condition等功能
不止C++,Java等语言基本都有这些接口,而且使用方法基本雷同
因此不再详细阐述,需要使用标准库的可以自己了解下


    std::mutex mutex;

    std::thread t1([&mutex]() {
        mutex.lock();
        cout << "Thread-1" << endl;
        cout << "Thread-1" << endl;
        cout << "Thread-1" << endl;
    });

    std::thread t2([&mutex]() {
        mutex.lock();
        cout << "Thread-2" << endl;
        cout << "Thread-2" << endl;
        cout << "Thread-2" << endl;
    });

    //t1和t2只有一个会打印成功,因为mutex没有执行unlock操作

发布了442 篇原创文章 · 获赞 45 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u013718730/article/details/103549805