Qt线程的简单使用(三)——通过一个实例理解QMutex的使用

参考资料:实例来源  Qt编程快速入门

Qt帮助手册关于QMutex、QThread等

首先先看一个示例,(示例程序来自,Qt编程快速入门,我做了一些修改)。效果图如下,程序开启了三个绘图线程分别往QImage上绘制三角形、圆和矩形。

主程序中开启了一个定时器,会定时将图片清空。除此主程序的paintEvent事件中,将这个图片通过QPainter绘制显示出来。在绘图线程中,当对QImage操作时,就需要进行加锁,以保证同时只有一个线程可以对QImage进行操作。

绘图线程的代码。

 1 #ifndef DRAWTHREADING_H
 2 #define DRAWTHREADING_H
 3 #include <QThread>
 4 #include <QWidget>
 5 #include <QMutex>
 6 class DrawThread:public QThread
 7 {
 8     Q_OBJECT
 9 public:
10     explicit  DrawThread(QWidget *parent = 0);
11     void SetShape(int inputshape);
12 protected:
13     void run();
14 private:
15     int shape;
16 };
17 
18 #endif // DRAWTHREADING_H
 1 #include "drawthreading.h"
 2 
 3 #include <QTime>
 4 #include <QImage>
 5 #include <QPainter>
 6 #include "mainwindow.h"
 7 
 8 DrawThread::DrawThread(QWidget *parent):QThread(parent)
 9 {
10     shape = 0;
11     QTime tm;
12     tm = QTime::currentTime();
13     qsrand(tm.msec());
14 }
15 void DrawThread::SetShape(int inputshape)
16 {
17     shape = inputshape;
18 }
19 void DrawThread::run()
20 {
21     QImage* parentScene = ((MainWindow*)(this->parent()))->m_image;
22 
23     while(true)
24     {
25         int x,y;
26         x = qrand()% parentScene->width();
27         y = qrand()% parentScene->height();
28         int r,g,b;
29         r = qrand() % 255;
30         g = qrand() % 255;
31         b = qrand() % 255;
32         ((MainWindow*)(this->parent()))->painterLock.lock();
33         QPainter painter;
34         painter.begin(parentScene);
35         QPen m_pen(QColor(r,g,b));
36         painter.setPen(m_pen);
37         switch(shape)
38         {
39             case 0:
40             painter.drawEllipse(x,y,(qrand() % 10) * 10,(qrand() % 10) * 10);
41             break;
42         case 1:
43             painter.drawRect(qrand() % parentScene ->width(),qrand() % parentScene->height(),(qrand()%10)* 15,(qrand()%10)* 15);
44             break;
45          case 2:
46             painter.drawLine(x-50,y,x+50,y);
47             painter.drawLine(x-50,y,x,y- 50);
48             painter.drawLine(x,y-50,x + 50,y);
49           default:
50             break;
51         }
52         painter.end();
53         ((MainWindow*)(this->parent()))->painterLock.unlock();
54          ((MainWindow*)(this->parent()))->update();
55         this->msleep(100);
56     }
57 }

注意在run()函数中,当对QImage操作时,使用到QMutex对象的lock,当绘图完成之后进行unlock()。

主工程代码:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 #include "ui_mainwindow.h"
 4 #include <QMainWindow>
 5 
 6 namespace Ui {
 7 class MainWindow;
 8 }
 9 #include"drawthreading.h"
10 #include <QMutex>
11 class MainWindow : public QMainWindow
12 {
13     Q_OBJECT
14 
15 public:
16     explicit MainWindow(QWidget *parent = 0);
17     ~MainWindow();
18 
19 private:
20    Ui::MainWindow ui;
21 public:
22     DrawThread drawTriangleThread;
23     DrawThread drawEcllipseThread;
24     DrawThread drawRectThread;
25     QImage* m_image;
26     QMutex painterLock;
27     void paintEvent(QPaintEvent*);
28     void timerEvent(QTimerEvent* event);
29 };
30 
31 #endif // MAINWINDOW_H
 1 #include "mainwindow.h"
 2 #include "ui_mainwindow.h"
 3 #include <QThread>
 4 #include <QImage>
 5 #include <QPainter>
 6 MainWindow::MainWindow(QWidget *parent) :
 7     QMainWindow(parent)
 8 {
 9     ui.setupUi(this);
10     this->setAutoFillBackground(true);
11     m_image = new QImage(1024,768,QImage::Format_RGB32);
12     QPainter painter(m_image);
13     QBrush fillBrush(QColor(255,255,255));
14 
15    painter.fillRect(0,0,1024,768,fillBrush);
16     this->startTimer(10000);
17 
18     drawEcllipseThread.SetShape(0);
19     drawEcllipseThread.setParent(this);
20     drawEcllipseThread.start();
21 
22     drawRectThread.SetShape(1);
23     drawRectThread.setParent(this);
24     drawRectThread.start();
25 
26     drawTriangleThread.SetShape(2);
27     drawTriangleThread.setParent(this);
28     drawTriangleThread.start();
29 }
30 
31 MainWindow::~MainWindow()
32 {
33     
34     if (drawEcllipseThread.isRunning())
35     {
36         drawEcllipseThread.quit();
37         drawEcllipseThread.wait();
38     }
39     if (drawRectThread.isRunning())
40     {
41         drawRectThread.quit();
42         drawRectThread.wait();
43     }
44     if (drawTriangleThread.isRunning())
45     {
46         drawTriangleThread.quit();
47         drawTriangleThread.wait();
48     }
49     if (m_image)
50     {
51         delete m_image;
52         m_image = NULL;
53     }    
54 }
55 
56 void MainWindow::paintEvent(QPaintEvent*)
57 {
58     QPainter painter(this);
59     painter.drawImage(0,0,*m_image);
60 
61 }
62 void MainWindow::timerEvent(QTimerEvent* event)
63 {
64     QPainter painter(m_image);
65     QBrush fillBrush(QColor(255,255,255));
66     painter.fillRect(0,0,1024,768,fillBrush);
67 }

我们在Qt帮助中查看QMutex,

The QMutex class provides access serialization between threads.

The purpose of a QMutex is to protect an object, data structure or section of code so that only one thread can access it at a time (this is similar to the Java synchronized keyword). It is usually best to use a mutex with a QMutexLocker since this makes it easy to ensure that locking and unlocking are performed consistently.

翻译过来,大致意思是,QMutex提供提供线程之间访问顺序化。QMutex目的是保护一个对象,数据结构或者一段代码以至于同一时间只能有一个线程访问。(与java中的关键字synchroized的类似)。Qt中的建议是使用QMutexLocker代替QMutex为了更容易的加锁、解锁。使用QMutexLocker对以上的代码进行调整,如下:

 

void DrawThread::run()
{
    QImage* parentScene = ((MainWindow*)(this->parent()))->m_image;

    //注意这里,使用QMutexLocker
    QMutexLocker locker(&((MainWindow*)(this->parent()))->painterLock);
    while(true)
    {
        int x,y;
        x = qrand()% parentScene->width();
        y = qrand()% parentScene->height();
        int r,g,b;
        r = qrand() % 255;
        g = qrand() % 255;
        b = qrand() % 255;
        //((MainWindow*)(this->parent()))->painterLock.lock();
        QPainter painter;
        painter.begin(parentScene);
        QPen m_pen(QColor(r,g,b));
        painter.setPen(m_pen);
        switch(shape)
        {
            case 0:
            painter.drawEllipse(x,y,(qrand() % 10) * 10,(qrand() % 10) * 10);
            break;
        case 1:
            painter.drawRect(qrand() % parentScene ->width(),qrand() % parentScene->height(),(qrand()%10)* 15,(qrand()%10)* 15);
            break;
         case 2:
            painter.drawLine(x-50,y,x+50,y);
            painter.drawLine(x-50,y,x,y- 50);
            painter.drawLine(x,y-50,x + 50,y);
          default:
            break;
        }
        painter.end();
        //((MainWindow*)(this->parent()))->painterLock.unlock();
         ((MainWindow*)(this->parent()))->update();
        this->msleep(100);
    }
}

至此,QMutex的方法已经讲诉完了,总之,QMutex提供提供线程之间访问顺序化。QMutex目的是保护一个对象,数据结构或者一段代码以至于同一时间只能有一个线程访问。

 

posted @ 2017-03-31 22:45  gongchao1212  阅读(12476)  评论(0编辑  收藏  举报