Qt多线程的实现方式
文章目录
- Qt多线程的实现方式
- QThread的run方法
- QObject的moveToThread
- QRunnalble的run
- QtConcurrent的run
- 线程同步
- 基于QMutex互斥同步
- 基于QReadWriteLock的线程同步
- 基于QWaitCondition的线程同步
QThread的run方法
一个QThread对象管理一个线程,一般从QThread继承一个自定义的类,并实现run方法,在run函数中实现线程需要完成的任务。QThread自身定义了started() 和finish()两个信号,started() 信号是在开始执行之前发射,finished()信号是在线程就要结束时发射。
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
// TODO
emit sigMsg(result);
}
signals:
void sigMsg(const QString &s);
};
void main()
{
WorkerThread *workerThread = new WorkerThread(this);
workerThread->start();
}
特点
1、优点:可以通过信号槽与外界进行通信。
2、缺点:每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。
3、适用场景:QThread适用于那些常驻内存的任务。
QObject的moveToThread
创建一个继承QObject的类MyThread,把要执行的计算放到一个函数中doWork,然后new一个Qthread,并把创建的myThread类movetothread到创建好的子线程中,然后start子线程,这样就实现了一个子线程。这里一定要通过信号去调用doWork函数。
class MyThread:public QObject
{
Q_OBJECT
public slots:
void doWork(){
int i=0;
while(i<4){
// ToDo
qDebug()<<QThread::currentThread()<<" "<<i++;
}
}
};
class MyTest :public QObject
{
Q_OBJECT
QThread workerThread;
public:
MyTest(){
MyThread* myThread = new MyThread(); // 这个任务函数不能有父对象,有父对象时不可以moveToThread
myThread->moveToThread(&workerThread);
workerThread.start();
connect(this,&MyTest::active,myThread,&MyThread::doWork); // 一定要通过槽函数去调用对应的函数,否则还是在主线程
}
~MyTest(){
workerThread.quit();
}
signals:
void active();
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
qDebug()<<QThread::currentThread();
ui->setupUi(this);
MyTest* t = new MyTest();
t->active();
QThread::sleep(3000);
t->deleteLater();
}
特点
一定要通过槽函数的形式去调用函数,要注意!你创建的QThread对象实例,仍然存活在主线程上,而非子线程。所以如果你直接调用其中的函数,那么还是在主线程上运行的。该方法并不是线程安全的。
QRunnalble的run
继承Qrunnable,并重写run虚函数,使用QThreadPool启动线程
class Runnable:public QRunnable
{
public:
Runnable();
~Runnable();
void run();
};
Runnable::Runnable():QRunnable()
{
}
Runnable::~Runnable()
{
cout<<"~Runnable()"<<endl;
}
void Runnable::run()
{
cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl;
cout<<"dosomething ...."<<endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
Runnable runObj;
QThreadPool::globalInstance()->start(&runObj);
returna.exec();
}
特点
1,无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
2,不能使用信号槽与外界通信。
3,QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。
QtConcurrent的run
使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。通过QtConcurrent::run()返回的QFuture不支持取消、暂停,返回的QFuture只能用于查询函数的运行/完成状态和返回值。
函数原型:
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
使用方式
QtConcurrent::run([=]() {
// TODO
});
线程同步
基于QMutex互斥同步
QMutex的目的是保护一个对象、数据结构或者代码段,所以同一时间只有一个线程可以访问它。如果使用Mutex锁那么多个线程在访问一段代码的时候是存在阻塞的,一个执行完毕下一个线程才会继续执行
- lock():试图锁定互斥量。如果另一个线程已经锁定这个互斥量,那么这次调用将阻塞直到那个线程把它解锁。
- unlock():进行解锁
- tryLock():试图锁定互斥量。如果锁被得到,这个函数返回真。如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止
QMutex mutex;
void DebugInfo()
{
mutex.lock();
qDebug("ABC");
qDebug("DEF");
mutex.unlock();
}
基于QReadWriteLock的线程同步
一种读写锁,用于保护可以进行读写访问的资源。这种索允许多个线程同时进行只读访问,但是一旦一个线程想要写入资源,则必须阻止所有其他线程,直到写入完成。
QReadWriteLock lock;
void ReaderThread::run()
{
...
lock.lockForRead();
read_file();
lock.unlock();
...
}
void WriterThread::run()
{
...
lock.lockForWrite();
write_file();
lock.unlock();
...
}
void lockForRead():锁定读取锁。如果另一个线程已锁定以进行写入,则此函数将阻塞当前线程。如果线程已经锁定写入,则无法锁定读取。void lockForWrite():锁定写入锁。如果另一个线程(包括当前线程)已锁定读取或写入,则此函数将阻塞当前线程。如果线程已经为读取而锁定,则不会为写入而锁定。
基于QWaitCondition的线程同步
QWaitCondition 允许线程在某些情况发生时唤醒另外的线程。一个或多个线程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == BufferSize)
bufferNotFull.wait(&mutex);
mutex.unlock();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
mutex.lock();
++numUsedBytes;
bufferNotEmpty.wakeAll();
mutex.unlock();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
mutex.unlock();
fprintf(stderr, "%c", buffer[i % BufferSize]);
mutex.lock();
--numUsedBytes;
bufferNotFull.wakeAll();
mutex.unlock();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}