




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Qt多线程概述Qt线程类Qt包含下面一些线程相关的类:QThread提供了开始一个新线程的方法QThreadStorage提供逐线程数据存储QMutex提供相互排斥的锁,或互斥量QMutexLocker是一个便利类,它可以自动对QMutex加锁与解锁QReadWriterLock提供了一个可以同时读操作的锁QReadLocker与QWriteLocker是便利类,它自动对QReadWriteLock加锁与解锁QSemaphore提供了一个整型信号量,是互斥量的泛化QWaitCondition提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠。Qt线程的创建Qt线程中有一个公共的抽象类,所
2、有的线程都是从这个QThread抽象类中派生的,要实现QThread中的纯虚函数run(),run()函数是通过start()函数来实现调用的。1 class MyThread:public QThread2 public 3virtual void run();4;5 6void MyThread:run()78 for int count=0;count 20;count+)9 sleep(1);10 qDebug(Ping!);111213 14 int main()1516 MyThread a;17 MyThread b;18 19 a.start();/自动调用run(),否则即使
3、该线程创建,也是一开始就挂起20 b.start();21/要等待线程a,b都退出22 a.wait();23 b.wait();2425 Qt线程同步1.QMutex QMutex(bool recursive=FALSE)virtualQMutex()void lock()/试图锁定互斥量。如果另一个线程已经锁定这个互斥量,那么这次调用将阻塞直到那个线程把它解锁。void unlock()bool locked()bool tryLock()/如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止,比如,它不是阻塞的。1/Qt 2QMutex mutex;3 vo
4、id someMethod()45 mutex.lock 6qDebug(Hello);7 qDebug(World);8 mutex.unlock();910 11/用Java的术语,这段代码应该是:12 void someMethod()1314 synchronized15 qDebug(Hello);16 qDebug(World);1718不过在Qt中我们可用通过另一个类来简化这种应用,因为如果使用QMutex.lock()而没有对应的使用QMutex.unlcok()的话就会造成死锁,别的线程永远也得不到接触该mutex锁住的共享资源的机会。尽管可以不使用lock()而使用tryL
5、ock(timeout)来避免因为死等而造成的死锁(tryLock(负值)=lock(),但是还是很有可能造成错误。对于上述的情况MFC中用CSingleLock或MultiLock,Boost中用boost:mutex:scoped_lock来进行解决,而在Qt中用QMutexLocker来进行解决。下面是没有采用QMutexLocker的例子和采用QMutexLocker的方案。2.QMutexLocker this complex function locks aQMutex upon entering the function and unlocks the mutex at all
6、the exit points 1int complexFunction(int flag)23 mutex.lock 45 int retVal=0;6 7switch(flag)8 case 09 case 110 mutex.unlock();11 return moreComplexFunction(flag);12 case 21314 int status=anotherFunction();15 if(status 0)16 mutex.unlock();17 return-2;1819 retVal=status+flag;2021 break;22 default 23 if
7、(flag 10)24 mutex.unlock();25 return-1;2627 break;2829 30 mutex.unlock();31 return retVal;32This example increases the likelihood that errors will occur.Using QMutexLocker greatly simplifies the code,and makes it more readable:1 int complexFunction(int flag)23 QMutexLocker locker(&mutex);4 5int retV
8、al=0;6 7switch(flag)8 case 09 case 110 return moreComplexFunction(flag);11 case 21213 int status=anotherFunction();14 if(status 015 return-2;16 retVal=status+flag;1718 break;19 default 20 if(flag 10 21 return-1;22 break;2324 25 return retVal;26Now,the mutex will always be unlocked when the QMutexLoc
9、ker object is destroyed(when the function returns since locker is an auto variable).即使在抛出异常的情况下也可以使用。3.QReadWriteLock用mutex进行线程同步有一个问题就是mutex只允许某个时刻只允许一个线程对共享资源进行访问,如果同时有多个线程对共享资源进行读访问,而只有一个写操作线程,那么在这种情况下如果采用mutex就成为程序运行性能的瓶颈了。在这种情况下Qt下采用QReadWriteLock来实现多个线程读,一个线程写。写线程执行的时候会阻塞所有的读线程,而读线程之间的运行不需要进行同
10、步。1 MyData data;2 QReadWriteLock lock;3 void ReaderThread:run()45 6lock.lockForRead();7 access_data_without_modifying_it(&data);8 lock.unlock();9 1011 void WriterThread:run()1213 14 lock.lockForWrite();15 modify_data(&data);16 lock.unlock();17 1819 20 QReadWriterLock与QMutex相似,除了它对read,write访问进行区别对待。
11、它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex,可以使得多线程程序更具有并发性。4.QReadLocker和QWriteLocker对于QMutex有QMutexLocker来简化使用,而对于QReadWriteLock有QReadLocker和QWriteLocker。Heres an example that uses QReadLocker to lock and unlock aread-write lock for reading:QReadWriteLock lock;QByteArray readData()QReadLocker locker
12、(&lock);return data;It is equivalent to the following code:QReadWriteLock lock;QByteArray readData()lock.lockForRead();lock.unlock();return data;5.QSemaphore QSemaphore是QMutex的一般化,它可以保护一定数量的相同资源,与此相对,一个mutex只保护一个资源。下面例子中,使用QSemaphore来控制对环状缓冲区的访问,此缓冲区被生产者线程和消费者线程共享。生产者不断向缓冲写入数据直到缓冲末端,消费者从缓冲不断从缓冲头部读取数
13、据。信号量比互斥量有更好的并发性,假如我们用互斥量来控制对缓冲的访问,那么生产者,消费者不能同时访问缓冲。然而,我们知道在同一时刻,不同线程访问缓冲的不同部分并没有什么危害。QSemaphore semaphore(1);|QMutex mutex;Qsemaphore.acquire();|Qmutex.lock();Qsemaphore.release();|Qmutex.unlock();Public Functions QSemaphore(int n=0)void acquire(int n=1)int available()constvoid release(int n=1)boo
14、l tryAcquire(int n=1)bool tryAcquire(int n,int timeout)Semaphores support two fundamental operations,acquire()andrelease acquire(n)tries to acquire nresources.If there arent that many resources available,the call will block until this is the case.release(n)releases nresources.returns immediately if
15、it cannot acquire the resourcesreturns the number of available resources at any time.Example:QSemaphore sem(5);/sem.available()=5 sem.acquire(3);/sem.available()=2 sem.acquire(2);/sem.available()=0 sem.release(5);/sem.available()=5 sem.release(5);/sem.available()=10 sem.tryAcquire(1);/sem.available(
16、)=9,returns true sem.tryAcquire(250);/sem.available()=9,returns false生产者线程写数据到buffer直到缓冲末端,然后重新从buffer的头部开始写。显然producer线程和consumer线程是需要进行同步的,Ifthe producer generates the data too fast,it will overwrite data that the consumer hasnt yet read;if the consumer reads the data too fast,it will pass the pro
17、ducer and read garbage.A crude way to solve this problem is to have the producer fill the buffer,then wait until the consumer has read the entire buffer,and so on.显然这样做效率是比较低的。1 const int DataSize=100000;2 const int BufferSize=8192;3 char bufferBufferSize;4 5/When the application starts,the reader t
18、hread will start/acquiringfreebytes and convert them intousedbytes 6QSemaphore freeBytes(BufferSize);/producer线程在此区域写入数据,初始资源数量为BufferSize 7QSemaphore usedBytes;/consumer线程读取此区域的数据,初始资源数量为0 89 10/For this example,each byte counts as one resource.11/In areal-world application,we would probably operat
19、e on larger/units(for example,64 or 256 bytes at atime)12 class Producer:public QThread 1314 public 15 void run();16;17/生产者每acquire一次就,使用掉Buffer个资源中的一个,而写入的字符存入到buffer数组中/从而消费者可用读取字符,从而消费者获取一个资源18 void Producer:run()1920/qsrand(QTime(0,0,0).secsTo(QTime:currentTime();21 for int i=0;i DataSize;+i)22
20、freeBytes.acquire();23 bufferi%BufferSize=ACGTint)qrand()%4;24 usedBytes.release();252627 28 class Consumer:public QThread 2930 public 31 void run();32;33 34 void Consumer:run()3536 for int i=0;i DataSize;+i)37 usedBytes.acquire();38 fprintf(stderr,%c,bufferi%BufferSize);39 freeBytes.release();4041
21、fprintf(stderr,n);4243/Finally,in main(),we start the producer and consumer threads./What happens then is that the producer converts somefreespace/intousedspace,and the consumer can then convert it back to/freespace.46 int main(int argc,char*argv)4748 QCoreApplication app(argc,argv);49 Producer prod
22、ucer;50 Consumer consumer;51 producer.start();52 consumer.start();53 producer.wait();54 consumer.wait();55 return 0;56producer的run函数:当producer线程执行run函数,如果buffer中已经满了,而没有consumer线程没有读,这样producer就不能再往buffer中写字符。此时在freeBytes.acquire处就阻塞直到consumer线程读(consume)数据。一旦producer获取到一个字节(资源)就写如一个随机的字符,并调用usedByt
23、es.release从而consumer线程获取一个资源可以读一个字节的数据了。consumer的run函数:当consumer线程执行run函数,如果buffer中没有数据,就是资源=0,则consumer线程在此处阻塞。直到producer线程执行写操作,写入一个字节,并执行usedBytes.release从而使得consumer线程的可用资源数=1。则consumer线程从阻塞状态中退出,并将usedBytes资源数-1,当前资源数=0。6.QWaitCondition bool wait(QMutex*mutex,unsigned long time=ULONG_MAX)void w
24、akeOne()void wakeAll()Public function:bool QWaitCondition:wait(QMutex*mutex,unsigned long time=ULONG_MAX)1)释放锁定的mutex 2)在线程对象上等待mutex必须由调用线程进行初锁定。注意调用wait的话,会自动调用unlock解锁之前锁住的资源,不然会造成死锁。线程1等待线程2来改变共享资源,从而达到一定的条件然后发出信号,使得线程1从wait中的阻塞状态中被唤醒。但是线程2想改变资源,却无法办到,因为线程1调用lock之后就在wait中blocking,了但是没有及时的unlock,
25、那么这就构成了死锁的条件。所以说wait函数除了使调用线程切换到内核态之外,还自动unlock(&mutex)mutex将被解锁,并且调用线程将会阻塞,直到下列条件之一满足时才醒来:另一个线程使用wakeOne()或wakeAll()传输信号给它。在这种情况下,这个函数将返回真。time毫秒过去了。如果time为ULONG_MAX(默认值),那么这个等待将永远不会超时(这个事件必须被传输)。如果等待的事件超时,这个函数将会返回假互斥量将以同样的锁定状态返回。这个函数提供的是允许从锁定状态到等待状态的原子转换。void QWaitCondition:wakeAll()这将会唤醒所有等待QWait
26、Condition的线程。这些线程被唤醒的顺序依赖于操组系统的调度策略,并且不能被控制或预知。void QWaitCondition:wakeOne()这将会唤醒所有等待QWaitCondition的线程中的一个线程。这个被唤醒的线程依赖于操组系统的调度策略,并且不能被控制或预知。假定每次用户按下一个键,我们有三个任务要同时执行,每个任务都可以放到一个线程中,每个线程的run()都应该是这样:QWaitCondition key_pressed;for(;)key_pressed.wait();/这是一个QWaitCondition全局变量/键被按下,做一些有趣的事do_something()
27、;或是这样:forevermutex.lock();keyPressed.wait(&mutex);do_something();mutex.unlock();第四个线程回去读键按下并且每当它接收到一个的时候唤醒其它三个线程,就像这样:QWaitCondition key_pressed;for(;)getchar();/在key_pressed中导致引起任何一个线程。wait()将会从这个方法中返回并继续执行key_pressed.wakeAll();注意这三个线程被唤醒的顺序是未定义的,并且当键被按下时,这些线程中的一个或多个还在do_something(),它们将不会被唤醒(因为它们现在
28、没有等待条件变量)并且这个任务也就不会针对这次按键执行操作。这种情况是可以避免得,比如,就像下面这样做:1 QMutex mymutex;2 QWaitCondition key_pressed;3 int mycount=0;4 5/Worker线程代码6 for(;)7 key_pressed.wait;/这是一个QWaitCondition全局变量/keyPressed.wait(&mutex);8 mymutex.lock 9mycount+;10 mymutex.unlock();11 do_something();12 mymutex.lock 13 mycount-;14 mym
29、utex.unlock();1516 17/读取按键线程代码18 for(;)19 getchar();20 mymutex.lock 21/睡眠,直到没有忙碌的工作线程才醒来。count=0说明没有Worker线程在do something 22 while(count 0)23 mymutex.unlock();24 sleep(1);25 mymutex.lock 2627 mymutex.unlock();28 key_pressed.wake All();2930应用条件变量对前面用信号量进行保护的环状缓冲区的例子进行改进:下面的例子中:1)生产者首先必须检查缓冲是否已满(numUs
30、edBytes=BufferSize),如果是,线程停下来等待bufferNotFull条件。如果不是,在缓冲中生产数据,增加numUsedBytes,激活条件bufferNotEmpty。2)使用mutex来保护对numUsedBytes的访问。另外,QWaitCondition:wait()接收一个mutex作为参数,这个mutex应该被调用线程初始化为锁定状态。在线程进入休眠状态之前,mutex会被解锁。而当线程被唤醒时,mutex会再次处于锁定状态。而且,从锁定状态到等待状态的转换是原子操作,这阻止了竞争条件的产生。当程序开始运行时,只有生产者可以工作。消费者被阻塞等待bufferNo
31、tEmpty条件,一旦生产者在缓冲中放入一个字节,bufferNotEmpty条件被激发,消费者线程于是被唤醒。1 const int DataSize=100000;2 const int BufferSize=8192;3 char bufferBufferSize;4 5QWaitCondition bufferNotEmpty;6 QWaitCondition bufferNotFull;7 QMutex mutex;8 int numUsedBytes=0;9 10 class Producer:public QThread 1112 public 13 void run();14;
32、15 16 void Producer:run()1718 qsrand(QTime(0,0,0).secsTo(QTime:currentTime();19 20 for int i=0;i DataSize;+i)21 mutex.lock/producer线程首先检查缓冲区是否已满22 if(numUsedBytes=BufferSize)/缓冲区已满,等待consumer来减少numUsedBytes/bufferNotFull.wait(&mutex)先调用mutex.unlock()然后收到信号时调用mutex.lock 23 bufferNotFull.wait(&mutex);
33、/缓冲区已满等待bufferNotFull的条件变量成立变为有信号24 mutex.unlock();25 26 bufferi%BufferSize=ACGTint)qrand()%4;27 28 mutex.lock 29+numUsedBytes;/producer用掉一个Bytes,表示producer写入buffer中的字节数30 bufferNotEmpty.wakeAll();31 mutex.unlock();323334 35 class Consumer:public QThread 3637 public 38 void run();39;40 41 void Consu
34、mer:run()4243 for int i=0;i DataSize;+i)44 mutex.lock 45 if(numUsedBytes=0 46 bufferNotEmpty.wait(&mutex);47 mutex.unlock();48 49 fprintf(stderr,%c,bufferi%BufferSize);50 51 mutex.lock 52-numUsedBytes;53 bufferNotFull.wakeAll();54 mutex.unlock();5556 fprintf(stderr,n);5758 59 int main(int argc,char*
35、argv)6061 QCoreApplication app(argc,argv);62 Producer producer;63 Consumer consumer;64 producer.start();65 consumer.start();66 producer.wait();67 consumer.wait();68 return 0;69另外一个例子:1#include qapplication.h 2#include qpushbutton.h 34/全局条件变量5 QWaitCondition mycond;6 7/Worker类实现8 class Worker:public
36、QPushButton,public QThread 910 Q_OBJECT 11 12 public 13 Worker(QWidget*parent=0,const char*name=0 14:QPushButton(parent,name)1516 setText(Start Working);17 18/连接从QPushButton继承来的信号和我们的slotClicked()方法19 connect(this,SIGNAL(clicked(),SLOT(slotClicked();20 21/调用从QThread继承来的start()方法这将立即开始线程的执行22 QThread:start();2324 25 public slots:26 void slotClicked()2728/唤醒等待这个条件变量的一个线程29 mycond.wakeOne();3031 32 protected 33 void run()3435/这个方法将被新创建的线程调用36 37 while(TRUE)38/锁定应用程序互斥锁,并且设置窗口标题来表明我们正在等待开始工作39 qApp lock 40 setC
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 初中化学课程跨学科实践活动案例设计与实施研究
- 生物化学(第4版)课件 第10章 基因的遗传和表达
- 基于脾肾互赞理论从miR-335-LATS1-YAP-β-catenin通路探讨补肾健脾方干预失重性OS的机制研究
- 电芯极耳超声焊接技术及应用
- 《社会财务共享服务实务》课件-领域1任务2-05.票据录入-费用类票据
- 灯具设计创新
- 健康秋天的果实
- 糖尿病的营养治疗与护理
- 肾内科护理教学
- 《网页设计与制作》课件-第8章Dreamweaver入门
- 中小学家长会期中期末家长会253
- 驱动电机与电机控制器
- 2024年便携式储能行业分析报告
- 医联体协议书(2024版)
- 2023年全国职业院校技能大赛-中药传统技能赛项规程
- 11 《爱莲说》对比阅读-2024-2025中考语文文言文阅读专项训练(含答案)
- 动物园野生动物驯养繁殖或驯养观赏可行性研究报告
- 煤矿开掘技术操作规程
- 2023年上海市长宁区高三年级下册二模英语试卷含详解
- 肺功能进修总结汇报
- GB/T 3428-2024架空导线用镀锌钢线
评论
0/150
提交评论