本文主要介绍Qt基金会开发的Qt多线程类Qt QThread和Qt timer类Qt Timer的具体方法和实例。有需要的可以参考以下。
Qt多线程
我们之前的程序都是单线程的,后来开始引入多线程。相当于以前一个人工作,现在很多人一起工作。
在Qt中使用多线程是非常必要的,因为Qt应用是事件驱动的,一旦一个事件处理程序耗时过长,其他事件就无法及时处理。
Qt使用QThread来管理线程。QThread对象是一个线程。QThread对象还有一个message sequential exec()函数,用于处理自己线程的事件。
Qt实现多线程有两种方式
1.Qt是创建线程的第一种方式
首先继承QThread
重写虚函数QThread:run
[虚拟保护] void QThread:run()
/*
*基类QThread的run函数只是启动exec()消息循环。
*/
例如:
#包括QApplication
#包含QThread
#包含QDebug
类MyThread:公共QThread
{
公共:
无效运行()
{
qDebug()“q thread begin”endl;
qDebug()“子线程”QThread:current threadid()endl;
q thread:sleep(5);
qDebug()“q thread end”endl;
exec();
}
};
int main(int argc,char** argv)
{
QApplication app(argc,argv);
MyThread线程;
thread . start();
qDebug()“主线程”QThread:current threadid()endl;
q thread:sleep(5);
qDebug()“主线程”QThread:current threadid()endl;
thread . quit();
qDebug()主线程thread . quit() endl;
tread . wait();
qDebug() main thread thread . wait() endl;
返回app . exec();
}
使用QThread的quit可以退出线程的消息循环。有时候不会马上退出,需要等到cpu的控制权回到线程的exec()上。
一般来说,当一个子线程退出时,主线程需要回收资源。可以调用QThread的wait,等待子线程退出,然后回收资源。
2、Qt第二种创建线程方式
继承QObject
实例化一个QThread对象
实现插槽功能。
QObject子类对象通过移动到thread将自己放入thread QThread对象中。
调用QThread对象的start函数启动线程。
slot函数必须通过发送信号在线程中执行,发送的信号存储在线程exec()消息队列中。
例如:
mywork.h
#ifndef MYWORK_H
#定义我的工作_H
#包含QThread
#包含QDebug
类MyWork:公共QObject
{
q _对象
公共插槽:
无效工作批次()
{
qDebug()“q thread begin”endl;
qDebug()“子线程”QThread:current threadid()endl;
q thread:sleep(5);
qDebug()“q thread end”endl;
}
};
#endif //MYWORK_H
widget.cpp
#包括QApplication
#包含QThread
#包含QDebug
#包含“mywork.h”
int main(int argc,char** argv)
{
qDebug()“主线程”QThread:current threadid()endl;
QApplication app(argc,argv);
QThread线程;
我的工作;
work.moveToThread(线程);
QObject:connect(thread,SIGNAL(started()),work,SLOT(work SLOT()));
thread . start();
q thread:sleep(6);
“qDebug()”线程正在运行“thread . is running()endl;
thread . quit();//调用quit使线程退出消息循环,否则线程将一直处于exec循环中。
thread . wait();//调用quit后立即调用wait回收线程资源。
“qDebug()”线程正在运行“thread . is running()endl;
返回app . exec();
}
应特别注意:
slot函数已经执行并进入线程exec()。您可以通过发送信号在线程中重新执行插槽函数。您也可以通过quit()退出线程exec()。
将调用moveToThread的QObject派生类对象不能指定主线程父对象托管内存。
QWidget和派生类对象的对象只能在GUI主线程中运行,不能使用moveToThread移动到子线程,即使没有指定父对象。
多线程对象内存释放
既然QObject对象不能托管内存对象,我们应该先释放线程对象还是QObject对象?
首先在线程循环中释放QObject(使用QObject:deleteLater函数),然后是QThread:quit,然后是QThread:wait。
例如:
mywork.h
#ifndef MYWORK_H
#定义我的工作_H
#包含QThread
#包含QDebug
类MyWork:公共QObject
{
q _对象
公共:
~ my work(){ qDebug()_ _ FUNCTION _ _ endl;}
公共插槽:
无效工作批次()
{
while(1)
{
qDebug() Work begin endl;
q thread:sleep(5);
qDebug()“work end”endl;
}
}
void otherWorkSlot()
{
qDebug()“other work begin”endl;
q thread:sleep(5);
qDebug()“other work end”endl;
}
};
#endif //MYWORK_H
widget.h
#ifndef WIDGET_H
#定义WIDGET_H
#包含QWidget
#包含“mywork.h”
类小部件:公共QWidget
{
q _对象
公共:
widget(q widget * parent=0);
~ Widget();
私人:
QThread * _ thread
MyWork * _ myWork
};
#endif //WIDGET_H
widget.cpp
#include widget.h
#包括q按钮
#包含“mywork.h”
#包含QThread
#包含QHBoxLayout
Widget:Widget(QWidget *parent)
:QWidget(父级)
{
_myWork=新my work;
_ thread=new q thread(this);
_ my work-moveToThread(_ thread);
_ thread-start();
q push button * pb0=new q push button( work ,this);
QPushButton *pb1=新的QPushButton(pb ,this);
qhbox layout * hBox=new qhbox layout(this);
hBox-add widget(pb0);
hBox-add widget(PB1);
this-set layout(hBox);
/*向另一个线程中的对象队列发送信号*/
connect(pb0,SIGNAL(clicked()),_myWork,SLOT(work SLOT()));
connect(pb1,SIGNAL(clicked()),_myWork,SLOT(otherWorkSlot()));
/*释放内存的建议用法*/
//connect(_thread,SIGNAL(finished()),_myWork,SLOT(delete later());
}
Widget:~Widget()
{
_ my work-delete later();//确保在QThread线程退出之前
_thread-quit()。
_thread-wait()。
}
3、Qt线程的同步
当多个线程同时访问一个资源时(例如,可以被多个线程操作的变量和函数等。),谁来使用这个资源是个问题。就像一大群人在抢同一个蛋糕。可能其中一个抢了,蛋糕被砸的可能性更大。在多线程中,这被称为竞争冒险。然后我们需要设置一个规则来约束大家,比如大家排队去拿蛋糕,这在多线程中叫同步法。
需要注意的是,同步不是同时的,而是有序的。
3.1、互斥锁Qt中的互斥体是QMutex,它不继承任何Qt基类。它使用QMutex来锁定共享资源。谁拿到钥匙,谁就有权利使用这个资源。其他线程会等待这个线程用完资源并返回密钥,然后它们会获取密钥。
例如:
QMutex互斥体;//此对象通常定义在多线程可以访问它的地方。
mutex . lock();//多个线程调用此函数获取锁。没有获得它的线程将阻塞并等待这个函数。
mutex . unlock();//释放锁定
qmtex: lock函数将让线程等待获取锁。如果不想等待,可以使用函数:
bool q mutex:try lock(int time out=0)
/*
*参数int timeout:等到超时毫秒,无论是否获取锁都会返回。超时为0时,直接返回。
*返回值true表示获取了锁,false表示没有获取。
*/
有时候我们会忘记释放锁,Qt也给我们提供了一个类QMutexLocker来管理锁。
QMutex互斥体;
void函数()
{
QMutexLocker锁(_ mutex);//最好将//QMutexLocker实例化为stack对象,在释放之前解锁QMutex。
}
在上面的例子中,一旦func()被执行,locker被自动释放,那么在释放它之前先解锁它。
3.2、信号量只有一个线程可以同时获得使用受互斥体保护的资源的权利。有些资源可以被多个线程同时访问,所以此时可以使用信号量。在Qt中,信号量是QSemaphore。
QSemaphore sem(2) //初始化信号量是2
SEM . acquire();//信号量位为0时,调用此函数会使信号量-1。一旦信号量为零,它将阻塞等待。
semaphore.release()。//制作信号量1
4、Qt定时器QTimer
定时器可以每隔一段时间发送一次信号,通过接收这个信号来处理一些定时任务。应该注意的是,计时器不会启动新的线程。Qt中的定时器由Qt Timer从QObject继承而来。
通过QTimer:start()启动定时器
无效开始时间(毫秒)
/*
*定时毫秒(毫秒)毫秒后,发射超时()信号
*/
通过链接信号超时()来处理一些定时任务,例如:
#include dialog.h
#包含QTimer
#包含qdebug.h
Dialog:Dialog(QWidget *parent)
:QDialog(父)
{
q timer * timer=新q timer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(do something());
定时器-启动(3000);
}
空的对话框* do something()
{
qDebug()_ _ FUNCTION _ _ endl;
}
对话框:~对话框()
{
}
本文主要介绍了夸脱多线程类q线程与夸脱定时器类QTimer的详细方法与实例,更多关于夸脱开发知识请查看下面的相关链接