Qt Gui 第十四章

一、QThread

当要运行多线程,则需要继承该类,并重写对应的run()函数;当启动线程执行run函数的时候,则需要调用start函数;等待run函数执行完毕则是wait函数;

当在run函数中,用一个变量来标记是否一直循环执行的时候;最好该标记使用volatile 该关键字,例如:volatile bool stopped;

该关键字可以使得stopped变量当数据改变的时候不会放进缓存里面,而是直接写入更改该变量;从而不会产生一些不可遇见的错误。

 

二、互斥锁

当要对某变量进行读取或者写入,处于线程安全的考虑时候可以使用互斥锁: QMutex;例如:
{
    mutex.lock();
    stopped = false;
    mutex.unlock();
}

这样有一个缺点是,当对stopped进行操作的时候发生了异常;则mutex一直处于锁定的状态;

{
    QMutexLocker locker(&mutex);
    stopped = false;
}

则不论stopped是否发生异常,mutex都不会出现死锁的现象。

三、信号锁和读写锁

复制代码
const int DataSize = 100000;
const int BufferSize = 4096;
char buffer[BufferSize];

QSemaphore freeSpace(BufferSize);
QSemaphore usedSpace(0);

Productor:
        for (int i = 0; i < DataSize; ++i) {
            freeSpace.acquire();
            buffer[i % BufferSize] = "ACGT"[uint(std::rand()) % 4];
            usedSpace.release();
        }

Consumer:
        for (int i = 0; i < DataSize; ++i) {
            usedSpace.acquire();
            std::cerr << buffer[i % BufferSize];
            freeSpace.release();
        }
复制代码

如上

freeSpace先初始化有BufferSize个信号量

Productor要写入数据的时候,先acquire一个信号量,即freeSpace减掉一个信号量。当写完之后;给usedSpace增加一个信号量:usedSpace.release();

Consumer则相反;

 

读写锁:即一个数据可以有多个线程同时读,但是只能有一个线程写;

复制代码
QReadWriteLock lock;

{
QReadLocker locker(&lock);
可以有多个线程同时进这里,执行读操作;
}

{
QWriteLocker wlocker(&locker);
执行写操作
只能有一个线程执行该操作;执行该操作的时候,不能执行读操作;
}
复制代码

 

三、QWaitCondition

 一般跟QMutex 一起使用;

复制代码
QWaitCondition bufferIsNotFull;
QWaitCondition bufferIsNotEmpty;
QMutex mutex;
int usedSpace = 0;

Productor:
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            while (usedSpace == BufferSize)
                bufferIsNotFull.wait(&mutex);
            buffer[i % BufferSize] = "ACGT"[uint(std::rand()) % 4];
            ++usedSpace;
            bufferIsNotEmpty.wakeOne();
            mutex.unlock();
        }

Consumer:
for (int i = 0; i < DataSize; ++i) { mutex.
lock(); while (usedSpace == 0) bufferIsNotEmpty.wait(&mutex); std::cerr << buffer[i % BufferSize]; --usedSpace; bufferIsNotFull.wakeAll(); mutex.unlock();
}
复制代码

当product调用wait时候,传入了mutex,则相当于:mutex.unlock(); 然后线程进入睡眠; customer的mutex.lock()则能继续执行;但完成操作之后,先将product的线程唤醒,然后mutex.unlock(); prodct被唤醒,则继续执行wait()剩余未完成的函数,即:mutex.lock();

bufferIsNotFull.wait(&mutex); 相当于执行 mutex.unlock() ->休眠->mutex.lock();

这里如果不加mutex的操作,则很容易出现,product跟customer双方时序混乱。

 

四、在子线程中修改主线程的ui显示;

1、通过信号槽的方式;

    connect(&thread, SIGNAL(transactionStarted(const QString&)),
            statusBar(), SLOT(showMessage(const QString&)));

线程中发射信号

emit transactionStarted(transact->message());

则主线程会执行相应的代码;

2、子线程保存UI对应的控件,通过主动调用信号槽的方式;

例如,在子线程中修改label的text,执行如下代码:

QMetaObject::invokeMethod(label, SLOT(setText(QString)), Q_ARG(QString,"Hello"));

则label的text会被修改为"Hello";


 

 

 

 

 

 

 

posted @   LCAC  阅读(133)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示