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目的是保护一个对象,数据结构或者一段代码以至于同一时间只能有一个线程访问。