如何进入dfu模式-pr2e打印机

qt多线程
2023年4月5日发(作者:云游戏平台)

Qt多线程编程总结(⼀)(所有GUI对象都是线程不安全的)

Qt对线程提供了⽀持,基本形式有独⽴于平台的线程类、线程安全⽅式的事件传递和⼀个全局Qt库互斥量允许你可以从不同的线程调⽤Qt⽅

法。

这个⽂档是提供给那些对多线程编程有丰富的知识和经验的听众的。推荐阅读:

警告:所有的GUI类(⽐如,QWidget和它的⼦类),操作系统核⼼类(⽐如,QProcess)和⽹络类都不是线程安全的。

QRegExp使⽤⼀个静态缓存并且也不是线程安全的,即使通过使⽤QMutex来保护的QRegExp对象。

线程类

最重要的类是QThread,也就是说要开始⼀个新的线程,就是开始执⾏你重新实现的QThread::run()。这和Java的线程类很相似。

为了写线程程序,在两个线程同时希望访问同⼀个数据时,对数据进⾏保护是很必要的。因此这⾥也有⼀个QMutex类,⼀个线程可以锁定

互斥量,并且在它锁定之后,其它线程就不能再锁定这个互斥量了,试图这样做的线程都会被阻塞直到互斥量被释放。例如:

classMyClass

{

public:

voiddoStuff(int);

private:

QMutexmutex;

inta;

intb;

};

//这⾥设置a为c,b为c*2。

voidMyClass::doStuff(intc)

{

();

a=c;

b=c*2;

();

}

这保证了同⼀时间只有⼀个线程可以进⼊MyClass::doStuff(),所以b将永远等于c*2。

另外⼀个线程也需要在⼀个给定的条件下等待其它线程的唤醒,QWaitCondition类就被提供了。线程等待的条件QWaitCondition指出发⽣了

什么事情,阻塞将⼀直持续到这种事情发⽣。当某种事情发⽣了,QWaitCondition可以唤醒等待这⼀事件的线程之⼀或全部。(这和POSIX

线程条件变量是具有相同功能的并且它也是Unix上的⼀种实现。)例如:

#include

#include

//全局条件变量

QWaitConditionmycond;

//Worker类实现

classWorker:publicQPushButton,publicQThread

{

Q_OBJECT

public:

Worker(QWidget*parent=0,constchar*name=0)

:QPushButton(parent,name)

{

setText("StartWorking");

//连接从QPushButton继承来的信号和我们的slotClicked()⽅法

connect(this,SIGNAL(clicked()),SLOT(slotClicked()));

//调⽤从QThread继承来的start()⽅法……这将⽴即开始线程的执⾏

QThread::start();

}

publicslots:

voidslotClicked()

{//唤醒等待这个条件变量的⼀个线程

e();

}

protected:

voidrun()

{

//这个⽅法将被新创建的线程调⽤……

while(TRUE){

//锁定应⽤程序互斥锁,并且设置窗⼝标题来表明我们正在等待开始⼯作

qApp->lock();

setCaption("Waiting");

qApp->unlock();

//等待直到我们被告知可以继续

();

//如果我们到了这⾥,我们已经被另⼀个线程唤醒……让我们来设置标题来表明我们正在⼯作

qApp->lock();

setCaption("Working!");

qApp->unlock();

//这可能会占⽤⼀些时间,⼏秒、⼏分钟或者⼏⼩时等等,因为这个⼀个和GUI线程分开的线程,在处理事件时,GUI线程不会停下来……

do_complicated_thing();

}

}

};

//主线程——所有的GUI事件都由这个线程处理。

intmain(intargc,char**argv)

{

QApplicationapp(argc,argv);

//创建⼀个worker……当我们这样做的时候,这个worker将在⼀个线程中运⾏

Workerfirstworker(0,"worker");

nWidget(&worker);

();

();

}

只要你按下按钮,这个程序就会唤醒worker线程,这个线程将会进⾏并且做⼀些⼯作并且然后会回来继续等待被告知做更多的⼯作。如果当

按钮被按下时,worker线程正在⼯作,那么就什么也不会发⽣。当线程完成了⼯作并且再次调⽤QWaitCondition::wait(),然后它就会被开

始。

线程安全的事件传递

在Qt中,⼀个线程总是⼀个事件线程——确实是这样的,线程从窗⼝系统中拉出事件并且把它们分发给窗⼝部件。静态⽅法

QThread::postEvent从线程中传递事件,⽽不同于事件线程。事件线程被唤醒并且事件就像⼀个普通窗⼝系统事件那样在事件线程中被分

发。例如,你可以强制⼀个窗⼝部件通过如下这样做的⼀个不同的线程来进⾏重绘:

QWidget*mywidget;

QThread::postEvent(mywidget,newQPaintEvent(QRect(0,0,100,100)));

这(异步地)将使mywidget重绘⼀块100*100的正⽅形区域。

Qt库互斥量

Qt库互斥量提供了从线程⽽不是事件线程中调⽤Qt⽅法的⼀种⽅法。例如:

QApplication*qApp;

QWidget*mywidget;

qApp->lock();

mywidget->setGeometry(0,0,100,100);

QPainterp;

(mywidget);

ne(0,0,100,100);

();

qApp->unlock();

在Qt中没有使⽤互斥量⽽调⽤⼀个函数通常情况下结果将是不可预知的。从另外⼀个线程中调⽤Qt的⼀个GUI相关函数需要使⽤Qt库互斥

量。在这种情况下,所有可能最终访问任何图形或者窗⼝系统资源的都是GUI相关的。使⽤容器类,字符串或者输⼊/输出类,如果对象只被

⼀个线程使⽤就不需要任何互斥量了。

告诫

当进⾏线程编程时,需要注意的⼀些事情:

当使⽤Qt库互斥量的时候不要做任何阻塞操作。这将会冻结事件循环。

确认你锁定⼀个递归QMutex的次数和解锁的次数⼀样,不能多也不能少。

在调⽤除了Qt容器和⼯具类的任何东西之前锁定Qt应⽤程序互斥量。

谨防隐含地共享类,你应该避免在线程之间使⽤操作符=()来复制它们。这将会在Qt的未来主要的或次要的发⾏版本中进⾏改进。

谨防那些没有被设计为线程安全的Qt类,例如,QPtrList的应⽤程序接⼝就不是线程安全的并且如果不同的线程需要遍历⼀个

QPtrList,它们应该在调⽤QPtrList::first()之前锁定并且在到达终点之后解锁,⽽不是在QPtrList::next()的前后进⾏锁定和解锁。

确认只在GUI线程中创建的继承和使⽤了QWidget、QTimer和QSocketNotifier的对象。在⼀些平台上,在某个不是GUI线程的线程中创

建这样的对象将永远不会接受到底层窗⼝系统的事件。

和上⾯很相似,只在GUI线程中使⽤QNetwork类。⼀个经常被问到的问题是⼀个QSocket是否可以在多线程中使⽤。这不是必须得,

因为所有的QNetwork类都是异步的。

不要在不是GUI线程的线程中试图调⽤processEvents()函数。这也包括QDialog::exec()、QPopupMenu::exec()、

QApplication::processEvents()和其它⼀些。

在你的应⽤程序中,不要把普通的Qt库和⽀持线程的Qt库混合使⽤。这也就是说如果你的程序使⽤了⽀持线程的Qt库,你就不应该连

接普通的Qt库、动态的载⼊普通Qt库或者动态地连接其它依赖普通Qt库的库或者插件。在⼀些系统上,这样做会导致Qt库中使⽤的静

态数据变得不可靠了。

QT通过三种形式提供了对线程的⽀持。它们分别是,⼀、平台⽆关的线程类,⼆、线程安全的事件投递,三、跨线程的信号-槽连接。这使

得开发轻巧的多线程Qt程序更为容易,并能充分利⽤多处理器机器的优势。多线程编程也是⼀个有⽤的模式,它⽤于解决执⾏较长时间的操

作⽽不⾄于⽤户界⾯失去响应。

Qt线程类

Qt包含下⾯⼀些线程相关的类:

QThread提供了开始⼀个新线程的⽅法

QThreadStorage提供逐线程数据存储

QMutex提供相互排斥的锁,或互斥量

QMutexLocker是⼀个便利类,它可以⾃动对QMutex加锁与解锁

QReadWriteLock提供了⼀个可以同时读写操作的锁

QReadLocker与QWriteLocker是便利类,它⾃动对QReadWriteLock加锁与解锁

QSemaphore提供了⼀个整型信号量,是互斥量的泛化

QWaitCondition提供了⼀种⽅法,使得线程可以在被另外线程唤醒之前⼀直休眠。

Qt⾼级线程类

QtConcurrent开启线程事务

QFutureWatcher观测线程状态

QFuture线程启动类

QThread创建线程

为创建⼀个线程,⼦类化QThread并且重写它的run()函数,例如:

classMyThread:publicQThread

{

Q_OBJECT

protected:

voidrun();

};

voidMyThread::run()

{

...

}

之后调⽤start,Qt即可创建⼀个线程,并在线程中执⾏run()函数中代码,注意UI⾮线程安全的。

QtConcurrent创建线程

QtConcurrent创建线程的⽅法⽐较多,⽽且QtConcurrent本⾝⽐较特殊,若系统有空闲线程时,它会调度空闲线程,⽆空闲线程时将会创

建⼀个线程。

(注意:QtConcurrent创建线程归QthreadPool管理,若超过最⼤线程数,将会进⼊队列等待),QtConcurrent创建线程的⽅法多种,以下

举例map函数:

QImagescale(constQImage&image)

{

qDebug()<<"Scalingimageinthread"<

(QSize(100,100),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);

}

intmain(intargc,char*argv[])

{

QApplicationapp(argc,argv);

constintimageCount=20;

//CreatealistcontainingimageCountimages.

QListimages;

for(inti=0;i

(QImage(1600,1200,QImage::Format_ARGB32_Premultiplied));

//UseQtConcurrentBlocking::mappedtoapplythescalefunctiontoallthe

//imagesinthelist.

QListthumbnails=QtConcurrent::blockingMapped(images,scale);

return0;

}

Qt线程同步

QMutex,QReadWriteLock,QSemaphore,QWaitCondition提供了线程同步的⼿段。使⽤线程的主要想法是希望它们可以尽可能并发执⾏,

⽽⼀些关键点上线程之间需要停⽌或等待。例如,假如两个线程试图同时访问同⼀个全局变量,结果可能不如所愿。

QMutex

QMutex提供相互排斥的锁,或互斥量。在⼀个时刻⾄多⼀个线程拥有mutex,假如⼀个线程试图访问已经被锁定的mutex,那么它将休眠,直

到拥有mutex的线程对此mutex解锁。Mutexes常⽤来保护共享数据访问。

QReadWriterLock

QReadWriterLock与QMutex相似,除了它对“read”,”write”访问进⾏区别对待。它使得多个读者可以共时访问数据。使⽤QReadWriteLock⽽

不是QMutex,可以使得多线程程序更具有并发性。

QReadWriteLocklock;

voidReaderThread::run()

{

rRead();

read_file();

();

}

voidWriterThread::run()

{

rWrite();

write_file();

();

}

QSemaphore

QSemaphore是QMutex的⼀般化,它可以保护⼀定数量的相同资源,与此相对,⼀个mutex只保护⼀个资源。下⾯例⼦中,使⽤

QSemaphore来控制对环状缓冲的访问,此缓冲区被⽣产者线程和消费者线程共享。⽣产者不断向缓冲写⼊数据直到缓冲末端,再从头开

始。消费者从缓冲不断读取数据。信号量⽐互斥量有更好的并发性,假如我们⽤互斥量来控制对缓冲的访问,那么⽣产者,消费者不能同时

访问缓冲。然⽽,我们知道在同⼀时刻,不同线程访问缓冲的不同部分并没有什么危害。

constintDataSize=100000;

constintBufferSize=8192;

charbuffer[BufferSize];

QSemaphorefreeBytes(BufferSize);

QSemaphoreusedBytes;

classProducer:publicQThread

{

public:

voidrun();

};

voidProducer::run()

{

qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

for(inti=0;i

e();

buffer[i%BufferSize]="ACGT"[(int)qrand()%4];

e();

}

}

classConsumer:publicQThread

{

public:

voidrun();

};

voidConsumer::run()

{

for(inti=0;i

e();

fprintf(stderr,"%c",buffer[i%BufferSize]);

e();

}

fprintf(stderr,"n");

}

intmain(intargc,char*argv[])

{

QCoreApplicationapp(argc,argv);

Producerproducer;

Consumerconsumer;

();

();

();

();

return0;

}

QWaitCondition

QWaitCondition允许线程在某些情况发⽣时唤醒另外的线程。⼀个或多个线程可以阻塞等待⼀QWaitCondition,⽤wakeOne()或wakeAll()设

置⼀个条件。wakeOne()随机唤醒⼀个,wakeAll()唤醒所有。

下⾯的例⼦中,⽣产者⾸先必须检查缓冲是否已满(numUsedBytes==BufferSize),如果是,线程停下来等待bufferNotFull条件。如果不是,

在缓冲中⽣产数据,增加numUsedBytes,激活条件bufferNotEmpty。使⽤mutex来保护对numUsedBytes的访问。另

外,QWaitCondition::wait()接收⼀个mutex作为参数,这个mutex应该被调⽤线程初始化为锁定状态。在线程进⼊休眠状态之前,mutex会

被解锁。⽽当线程被唤醒时,mutex会处于锁定状态,⽽且,从锁定状态到等待状态的转换是原⼦操作,这阻⽌了竞争条件的产⽣。当程序开

始运⾏时,只有⽣产者可以⼯作。消费者被阻塞等待bufferNotEmpty条件,⼀旦⽣产者在缓冲中放⼊⼀个字节,bufferNotEmpty条件被激

发,消费者线程于是被唤醒。

constintDataSize=100000;

constintBufferSize=8192;

charbuffer[BufferSize];

QWaitConditionbufferNotEmpty;

QWaitConditionbufferNotFull;

QMutexmutex;

intnumUsedBytes=0;

classProducer:publicQThread

{

public:

voidrun();

};

voidProducer::run()

{

qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

for(inti=0;i

();

if(numUsedBytes==BufferSize)

(&mutex);

();

buffer[i%BufferSize]="ACGT"[(int)qrand()%4];

();

++numUsedBytes;

l();

();

}

}

classConsumer:publicQThread

{

public:

voidrun();

};

voidConsumer::run()

{

for(inti=0;i

();

if(numUsedBytes==0)

(&mutex);

();

fprintf(stderr,"%c",buffer[i%BufferSize]);

();

--numUsedBytes;

l();

();

}

fprintf(stderr,"n");

}

intmain(intargc,char*argv[])

{

QCoreApplicationapp(argc,argv);

Producerproducer;

Consumerconsumer;

();

();

();

();

return0;

}

更多推荐

qt多线程