资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,2,*,第,12,章,Qt 5,多线程,12.1,多线程的简单实现,12.2,多线程控制,12.3,多线程应用的例子,12.1,多线程的简单实现,(,1,)在头文件“,threaddlg.h,”中声明用于界面显示所需的控件,其具体代码如下:,#include,#include,class ThreadDlg:public QDialog,Q_OBJECT,public:,ThreadDlg(QWidget*parent=0);,ThreadDlg();,private:,QPushButton*startBtn;,QPushButton*stopBtn;,QPushButton*quitBtn;,;,12.1,多线程的简单实现,(,2,)在源文件“,threaddlg.cpp,”的构造函数中,完成各个控件的初始化工作,其具体代码如下:,#include threaddlg.h,#include,ThreadDlg:ThreadDlg(QWidget*parent),:QDialog(parent),setWindowTitle(tr(,线程,);,startBtn=new QPushButton(tr(,开始,);,stopBtn=new QPushButton(tr(,停止,);,quitBtn=new QPushButton(tr(,退出,);,QHBoxLayout*mainLayout=new QHBoxLayout(this);,mainLayout-addWidget(startBtn);,mainLayout-addWidget(stopBtn);,mainLayout-addWidget(quitBtn);,12.1,多线程的简单实现,(,3,)此时运行程序,界面显示如图,12.1,所示。,12.1,多线程的简单实现,以上完成了界面的设计,下面的内容是具体的功能实现。,(,1,)在头文件“,workthread.h,”中,工作线程,WorkThread,类继承自,QThread,类。重新实现,run(),函数。其具体代码如下:,#include,class WorkThread:public QThread,Q_OBJECT,public:,WorkThread();,protected:,void run();,;,12.1,多线程的简单实现,(,2,)在源文件“,workthread.cpp,”中添加具体实现代码如下:,#include workthread.h,#include,WorkThread:WorkThread(),run(),函数实际上是一个死循环,它不停地打印数字,09,。为了显示效果明显,程序将每一个数字重复打印,8,次。,void WorkThread:run(),while(true),for(int n=0;n10;n+),qDebug()nnnnnnnn;,12.1,多线程的简单实现,(,3,)在头文件“,threaddlg.h,”中添加以下内容:,#include workthread.h,#define MAXSIZE 1,public slots:,void slotStart();,void slotStop();,private:,WorkThread*workThreadMAXSIZE;,12.1,多线程的简单实现,(,4,)在源文件“,threaddlg.cpp,”中添加以下内容:,其中,,在构造函数中添加如下代码:,connect(startBtn,SIGNAL(clicked(),this,SLOT(slotStart();,connect(stopBtn,SIGNAL(clicked(),this,SLOT(slotStop();,connect(quitBtn,SIGNAL(clicked(),this,SLOT(close();,槽函数,slotStart(),,当用户单击“开始”按钮时,此函数将被调用。这里使用两个循环,目的是为了使新建的线程尽可能同时开始执行,其具体实现代码如下:,void ThreadDlg:slotStart(),for(int i=0;iMAXSIZE;i+),workThreadi=new WorkThread();,for(int i=0;istart();,startBtn-setEnabled(false);,stopBtn-setEnabled(true);,12.1,多线程的简单实现,槽函数,slotStop(),,当用户单击“停止”按钮时,此函数将被调用。其具体实现代码如下:,void ThreadDlg:slotStop(),for(int i=0;iterminate();,workThreadi-wait();,startBtn-setEnabled(true);,stopBtn-setEnabled(false);,12.1,多线程的简单实现,(,5,)运行结果如图,12.2,所示。,12.2,多线程控制,下面举一个例子来说明问题:,class Key,public:,Key()key=0;,int creatKey()+key;return key;,int value()const return key;,private:,int key;,;,12.2,多线程控制,虽然类,Key,产生主键的函数,creatKey(),只有一条语句执行修改成员变量,key,的值,但是,C+,的“,+,”操作符并不是原子操作,通常编译后它将被展开成为以下三条机器命令:,将变量值载入寄存器;,将寄存器中的值加,1,;,将寄存器中的值写回主存。,12.2.1,互斥量,1,QMutex,类,QMutex,类还提供了一个,tryLock(),函数,如果互斥量已被锁定,则立即返回。,例如:,class Key,public:,Key()key=0;,int creatKey()mutex.lock();+key;return key;mutex.unlock();,int value()const mutex.lock();return key;mutex.unlock();,private:,int key;,QMutex mutex;,;,12.2.1,互斥量,2,QMutexLocker,类,例如:,class Key,public:,Key()key=0;,int creatKey()QMutexLocker.locker(,int value()const QMutexLocker.locker(,private:,int key;,QMutex mutex;,;,12.2.2,信号量,生产者,/,消费者实例中对同步的需求有两处:,(,1,)如果生产者过快地生产数据,将会覆盖消费者还没有读取的数据。,(,2,)如果消费者过快地读取数据,将越过生产者并且读取到一些过期数据。,针对以上问题,可以有两种解决方法:,(,1,)首先使生产者填满整个缓冲区,然后等待消费者读取整个缓冲区,这是一个比较笨拙的方法。,(,2,)使生产者和消费者线程同时分别操作缓冲区的不同部分,这是一种比较高效的方法。,12.2.2,信号量,(,1,)源文件“,main.cpp,”中添加的具体实现代码如下:,#include,#include,#include,#include,const int DataSize=1000;,const int BufferSize=80;,int bufferBufferSize;,QSemaphore freeBytes(BufferSize);,QSemaphore usedBytes(0);,12.2.2,信号量,(,2,),Producer,类继承自,QThread,类,作为生产者类,其声明如下:,class Producer:public QThread,public:,Producer();,void run();,;,Producer,构造函数中没有实现任何内容:,Producer:Producer(),12.2.2,信号量,Producer:run(),函数的具体实现代码如下:,void Producer:run(),for(int i=0;iDataSize;i+),freeBytes.acquire();,bufferi%BufferSize=(i%BufferSize);,usedBytes.release();,12.2.2,信号量,(,3,),Consumer,类继承自,QThread,类,作为消费者类,其声明如下:,class Consumer:public QThread,public:,Consumer();,void run();,;,Consumer,构造函数中没有实现任何内容:,Consumer:Consumer(),12.2.2,信号量,Consumer:run(),函数的具体实现代码如下:,void Consumer:run(),for(int i=0;iDataSize;i+),usedBytes.acquire();,fprintf(stderr,%d,bufferi%BufferSize);,if(i%16=0&i!=0),fprintf(stderr,n);,freeBytes.release();,fprintf(stderr,n);,12.2.2,信号量,(,4,),main(),函数的具体内容如下:,int main(int argc,char*argv),QCoreApplication a(argc,argv);,Producer producer;,Consumer consumer;,producer.start();,consumer.start();,producer.wait();,consumer.wait();,return a.exec();,12.2.2,信号量,(,5,)最终运行结果如图,12.3,所示。,12.2.3,线程等待与唤醒,源文件“,main.cpp,”的具体内容如下:,#include,#include,#include,#include,#include,const int DataSize=1000;,const int BufferSize=80;,int bufferBufferSize;,QWaitCondition bufferEmpty;,QWaitCondition bufferFull;,QMutex mutex;,int numUsedBytes=0;,int rIndex=0;,12.2.3,线程等待与唤醒,生产者线程,Producer,类继承自,QThread,类,其声明如下:,class Producer:public QThread,public:,Producer();,void run();,;,Producer,构造函数无须实现:,Producer:Producer(),12.2.3,线程等待与唤醒,Producer:run(),函数的具体内容如下:,void Producer:run(),for(int i=0;istart(),函数后,此虚函数开始执行,其具体代码如下:,void TimeThread:run(),QTcpSocket tcpSocket;,if(!tcpSocket.setSocketDescriptor(socketDescriptor),emit error(tcpSocket.error();,return;,QByteArray block;,QDataStream out(,out.setVersion(QDataStream:Qt_4_3);,uint time2u=QDateTime:currentDateTime().toTime_t();,outstart();,12.3.1,服务器,(,8,)在服务器端界面的头文件“,dialog.h,”中添加的具体代码如下:,class TimeServer;,public slots:,void slotShow();,private:,TimeServer*timeServer;,int count;,12.3.1,服务器,(,9,)在源文件“,dialog.cpp,”中,添加的头文件如下:,#include,#include timeserver.h,其中,,在,Dialog,类的构造函数中添加的内容,用于启动服务器端的网络监听,其具体实现如下:,count=0;,timeServer=new TimeServer(this);,if(!timeServer-listen(),QMessageBox:critical(this,tr(,多线程时间服务器,),tr(,无法启动服务器:,%1.).arg(timeServer-errorString();,close();,return;,Label1-setText(tr(,服务器端口:,%1.).arg(timeServer-serverPort();,12.3.1,服务器,在源文件“,dialog.cpp,”中,槽函数,slotShow(),的具体内容如下:,void Dialog:slotShow(),Label2-setText(tr(,第,%1,次请求完毕。,).arg(+count);,12.3.1,服务器,(,10,)在服务器端工程文件“,TimeServer.pro,”中添加如下代码:,QT+=network,(,11,)最后运行服务器端工程“,TimeServer.pro,”,结果如图,12.6,所示。,12.3.2,客户端,下面是客户端的实现,界面效果如图,12.7,所示。,12.3.2,客户端,(,1,)建立客户端工程“,TimeClient.pro,”。在头文件“,timeclient.h,”中,定义了客户端界面类,TimeClient,继承自,QDialog,类,其具体代码。,(,2,)在源文件“,timeclient.cpp,”中,,TimeClient,类的构造函数完成了初始化,界面,其具体代码。,在源文件“,timeclient.cpp,”中,,enableGetBtn(),函数的具体代码如下:,void TimeClient:enableGetBtn(),getBtn-setEnabled(!serverNameLineEdit-text().isEmpty()&,!portLineEdit-text().isEmpty();,12.3.2,客户端,在源文件“,timeclient.cpp,”中,,getTime(),函数的具体代码如下:,void TimeClient:getTime(),getBtn-setEnabled(false);,time2u=0;,tcpSocket-abort();,tcpSocket-connectToHost(serverNameLineEdit-text(),portLineEdit-text().toInt();,12.3.2,客户端,在源文件“,timeclient.cpp,”中,,readTime(),函数的具体代码如下:,void TimeClient:readTime(),QDataStream in(tcpSocket);,in.setVersion(QDataStream:Qt_4_3);,if(time2u=0),if(tcpSocket-bytesAvailable()time2u;,dateTimeEdit-setDateTime(QDateTime:fromTime_t(time2u);,getBtn-setEnabled(true);,12.3.2,客户端,在源文件“,timeclient.cpp,”中,,showError(),函数的具体代码如下:,void TimeClient:showError(QAbstractSocket:SocketError socketError),switch(socketError),case QAbstractSocket:RemoteHostClosedError:,break;,case QAbstractSocket:HostNotFoundError:,QMessageBox:information(this,tr(,时间服务客户端,),tr(,主机不可达!,);,break;,case QAbstractSocket:ConnectionRefusedError:,QMessageBox:information(this,tr(,时间服务客户端,),tr(,连接被拒绝!,);,break;,default:,QMessageBox:information(this,tr(,时间服务客户端,),tr(,产生如下错误,:%1.).arg(tcpSocket-errorString();,getBtn-setEnabled(true);,12.3.2,客户端,(,3,)在客户端工程文件“,TimeClient.pro,”中,添加如下代码:,QT+=network,(,4,)运行客户端工程“,TimeClient.pro,”,显示界面如图,12.7,所示。,最后,同时运行服务器和客户端程序,单击客户端“获取时间”按钮,从服务器上获得当前的系统时间,如图,12.8,所示。,
展开阅读全文