QT学习(五)QT之多线程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/anpingbo/article/details/57477810

1.  线程同时进行

QT提供了QThread来定义一个线程,我们通过定义类thread来重新实现它。

classThread:publicQThread

{

    Q_OBJECT

 

public:

    Thread();

 

    voidsetMessage(constQString&Message);

    voidstop();

 

protected:

    voidrun();

 

private:

    QStringMessageStr;

    volatileboolstopped;

 

 

};

线程通过判断stopped变量来选择打印信息,重新实现了run函数和stop函数,用于线程运行和终止。

voidThread::run(){
    while(!stopped)
        std::cerr<<qPrintable(MessageStr);
    stopped=false;
    std::cerr<<std::endl;
 
}
voidThread::stop(){
    stopped=true;
}

接下来制作一个简单的对话框,实现两个按钮,可以调用线程。这两个线程可以同时运行,互不相关,有各自的stopped判别变量。

public:
    threadDialog(QWidget*parent=0);
 
privateslots:
    voidstartOrStopThreadA();
    voidstartOrStopThreadB();
protected:
    voidcloseEvent(QCloseEvent*closeEvent);
 
private:
    ThreadthreadA;
    ThreadthreadB;
    QPushButton*threadButtonA;
    QPushButton*threadButtonB;
    QPushButton*quitButton;

这是类的定义,包括定义了三个按钮,分别用于启动线程A和B以及停止两个线程的按钮。


当同时按下A 和B线程的按钮,会交替输出A和B的信息。

2.  互斥锁

QT通过QMutex来建立保护多线程共同访问区,通过lock和unlock分别来锁住和解锁一段变量或者代码。当引用lock时,其后边的代码都被当前进程占用,其他进程不能访问,直到调用unlock后,其他程序才可以调用。上面代码可以修改为:

voidThread::run(){
    forever{
        mutex.lock();
        if(stopped){
            stopped=false;
            mutex.unlock();
            break;
        }
        mutex.unlock();
        std::cerr<<qPrintable(MessageStr);
    }
    std::cerr<<std::endl;
}
 
 
voidThread::stop(){
    mutex.lock();
    stopped=true;
    mutex.unlock();
}

3.  信号量

信号量可以用于当两个线程之间传递大量数据时,两个线程通过信号量来确定缓冲区大小,以及自己是否可以进行读写操作。读写缓冲区是典型的生产者和消费者类型,生产者向缓冲区写数据,消费者读取数据。现在定义两个信号量,一个检测缓冲区可以写的空间大小,一个记录可以读的空间大小,这类似于FIFO。

QSemaphorefreeSpace(BufferSize);

QSemaphoreusedSpace(0);

信号量通过acquire来获得可用区域,而通过release来释放已经完成的区域。

voidProducer::run()
{
    for(inti=0;i<DataSize;++i){
        freeSpace.acquire();
        buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
        usedSpace.release();
    }
}
voidConsumer::run()
{
    for(inti=0;i<DataSize;++i){
        usedSpace.acquire();
        std::cerr<<buffer[i%BufferSize];
        freeSpace.release();
    }
    std::cerr<<std::endl;
}

生产者每次写入一个数据,usedSpace就增加一个可读数据。而消费者每次读一个数据,freeSpace就增加一个自由空间。

4.  等待条件

除了使用信号量,还可以通过QWaitCondition定义等待变量,用于线程开启和等待。

QWaitConditionbufferIsNotFull;

QWaitConditionbufferIsNotEmpty;

QMutexmutex;

intusedSpace=0;

以上bufferIsNotFull和bufferIsNotEmpty可以来唤起和阻塞进程。而usedSpace则用于计算可用空间。

voidProducer::run()
{
    for(inti=0;i<DataSize;++i){
        mutex.lock();
        while(usedSpace==BufferSize)
            bufferIsNotFull.wait(&mutex);
        buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
        ++usedSpace;
        bufferIsNotEmpty.wakeAll();
        mutex.unlock();
    }
}
voidConsumer::run()
{
    for(inti=0;i<DataSize;++i){
        mutex.lock();
        while(usedSpace==0)
            bufferIsNotEmpty.wait(&mutex);
        std::cerr<<buffer[i%BufferSize];
        --usedSpace;
        bufferIsNotFull.wakeAll();
        mutex.unlock();
    }
    std::cerr<<std::endl;
}

生产者如果检测到空间已经写满,就会被bufferIsNotFull阻塞等待,直到消费者通过消费空间,使得usedSpace不满为止。然后生产者写入一个数据,可用空间就增加一个。消费者正好与生产者相反。

猜你喜欢

转载自blog.csdn.net/anpingbo/article/details/57477810