QT之多线程

1.  线程同时进行

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

class Thread:public QThread

{

    Q_OBJECT

 

public:

    Thread();

 

    void setMessage(const QString &Message);

    void stop();

 

protected:

    void run();

 

private:

    QString MessageStr;

    volatile bool stopped;

 

 

};

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

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

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

public:
    threadDialog(QWidget *parent = 0);
 
private slots:
    void startOrStopThreadA();
    void startOrStopThreadB();
protected:
    void closeEvent(QCloseEvent *closeEvent);
 
private:
    Thread threadA;
    Thread threadB;
    QPushButton *threadButtonA;
    QPushButton *threadButtonB;
    QPushButton *quitButton;

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

 

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

2.  互斥锁

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

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

3.  信号量

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

QSemaphore freeSpace(BufferSize);

QSemaphore usedSpace(0);

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

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

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

4.  等待条件

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

QWaitCondition bufferIsNotFull;

QWaitCondition bufferIsNotEmpty;

QMutex mutex;

int usedSpace = 0;

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

void Producer::run()
{
    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.wakeAll();
        mutex.unlock();
    }
}

 

 

void Consumer::run()
{
    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();
    }
    std::cerr << std::endl;
}

 

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

 

posted @ 2017-02-26 21:14  苹果不削皮  阅读(185)  评论(0编辑  收藏  举报