目录
QT的Qthread的一些机制和用法
- QT提供QThread类以进行多任务处理。与多任务处理一样,Qt提供的线程可以做到单个线程做不到的事情。例如,网络应用程序中,可以使用线程处理多种连接器。
- QThread继承自QObject类,且提供QMutex类以实现同步。线程和进程共享全局变量,可以使用互斥体对改变后的全局变量值实现同步。因此,必须编辑全局数据时,使用互斥体实现同步,其它进程则不能改变或浏览全局变量值。
- Qt中实现多线程有两种方法:一种是派生QThread类对象的方法,即自定义一个类继承自QThread,并重写run()函数,在run()函数中实现线程要执行的任务;另一种是使用QThread对象的方法,即创建一个QThread对象,并将一个继承自QObject的类的对象移动到该线程中,在该类中定义信号和槽函数来实现线程间的通信和任务执行。
- Qt中的线程可以设置优先级、退出、等待、终止等操作,也可以通过信号和槽机制来监听线程的开始和结束事件。Qt还提供了一些静态函数来获取当前线程、理想线程数、线程休眠等信息。
- Qt还提供了一些其他的类来辅助线程的管理和同步,如QRunnable、QThreadPool、QFuture、QSemaphore、QWaitCondition等。
下面,我们就来演示一哈,如何使用QThread(例子1):
- 首先,定义一个Worker类,继承自QObject,并声明一个槽函数doWork()和一个信号resultReady()。
- 然后,在Worker类的构造函数中,使用qDebug()输出当前线程的地址,以便观察线程的变化。
- 接着,在doWork()槽函数中,使用QThread::sleep()模拟一个耗时操作,并在完成后发射resultReady()信号。
- 然后,创建一个QThread对象和一个Worker对象,并使用QObject::moveToThread()将Worker对象移动到QThread对象中。
- 接着,使用QObject::connect()连接QThread对象的started()信号和Worker对象的doWork()槽函数,以及Worker对象的resultReady()信号和QThread对象的quit()槽函数。
- 最后,使用QThread::start()启动线程,并使用QThread::wait()等待线程结束!
具体的代码实现如下:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr) : QObject(parent)
{
qDebug() << "Worker created in thread" << QThread::currentThread();
}
public slots:
void doWork()
{
qDebug() << "Worker started in thread" << QThread::currentThread();
QThread::sleep(5); // simulate a heavy task
qDebug() << "Worker finished in thread" << QThread::currentThread();
emit resultReady();
}
signals:
void resultReady();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread" << QThread::currentThread();
QThread *thread = new QThread(); // create a thread object
Worker *worker = new Worker(); // create a worker object
worker->moveToThread(thread); // move the worker to the thread
QObject::connect(thread, &QThread::started, worker, &Worker::doWork); // connect the thread started signal to the worker doWork slot
QObject::connect(worker, &Worker::resultReady, thread, &QThread::quit); // connect the worker resultReady signal to the thread quit slot
QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater); // connect the thread finished signal to the worker deleteLater slot
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); // connect the thread finished signal to the thread deleteLater slot
thread->start(); // start the thread
thread->wait(); // wait for the thread to finish
qDebug() << "Main thread finished";
return a.exec();
}
运行上面的代码,结果如下:
Main thread QThread(0x7ffedf9a1b80)
Worker created in thread QThread(0x7ffedf9a1b80)
Worker started in thread QThread(0x55c3c8f3c4c0)
Worker finished in thread QThread(0x55c3c8f3c4c0)
Main thread finished
可以看到,Worker对象最初是在主线程中创建的,但是在执行doWork()槽函数时,已经移动到了子线程中。当doWork()槽函数完成后,发射了resultReady()信号,导致子线程退出,并删除了Worker对象和子线程对象。主线程在等待子线程结束后,也正常退出了。这就是一个简单的多线程的例子。希望对你有帮助。😊
总的来说Qthread的实现是:
- 创建一个QThread对象和一个继承自QObject的类的对象,这个类中定义了要在线程中执行的任务和信号。
- 使用QObject::moveToThread()将继承自QObject的类的对象移动到QThread对象中,这样就可以在子线程中执行任务了。
- 使用QObject::connect()连接QThread对象的信号和继承自QObject的类的对象的槽函数,以及继承自QObject的类的对象的信号和QThread对象的槽函数,以实现线程间的通信和控制。
- 使用QThread::start()启动线程,并使用QThread::wait()等待线程结束。
下面是一个使用QThread的计时器触发的例子(例子2):
- 首先,定义一个TimerWorker类,继承自QObject,并声明一个槽函数onTimeout()和一个信号timeout()。
- 然后,在TimerWorker类的构造函数中,创建一个QTimer对象,并将其timeout()信号连接到自身的onTimeout()槽函数和timeout()信号。
- 接着,在onTimeout()槽函数中,使用qDebug()输出当前线程的地址和当前时间,以便观察计时器的触发情况。
- 然后,创建一个QThread对象和一个TimerWorker对象,并使用QObject::moveToThread()将TimerWorker对象移动到QThread对象中。
- 接着,使用QObject::connect()连接QThread对象的started()信号和TimerWorker对象的start()槽函数,以及TimerWorker对象的timeout()信号和主线程的槽函数。
- 最后,使用QThread::start()启动线程,并使用QThread::wait()等待线程结束。
代码如下:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
#include <QDateTime>
class TimerWorker : public QObject
{
Q_OBJECT
public:
explicit TimerWorker(QObject *parent = nullptr) : QObject(parent)
{
qDebug() << "TimerWorker created in thread" << QThread::currentThread();
m_timer = new QTimer(this); // create a timer
m_timer->setInterval(1000); // set the interval to 1 second
connect(m_timer, &QTimer::timeout, this, &TimerWorker::onTimeout); // connect the timer timeout signal to the onTimeout slot
connect(m_timer, &QTimer::timeout, this, &TimerWorker::timeout); // connect the timer timeout signal to the timeout signal
}
public slots:
void start()
{
m_timer->start(); // start the timer
}
void onTimeout()
{
qDebug() << "TimerWorker timeout in thread" << QThread::currentThread();
qDebug() << "Current time:" << QDateTime::currentDateTime().toString();
}
signals:
void timeout();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread" << QThread::currentThread();
QThread *thread = new QThread(); // create a thread object
TimerWorker *worker = new TimerWorker(); // create a worker object
worker->moveToThread(thread); // move the worker to the thread
QObject::connect(thread, &QThread::started, worker, &TimerWorker::start); // connect the thread started signal to the worker start slot
QObject::connect(worker, &TimerWorker::timeout, [](){qDebug() << "Main thread received timeout signal";}); // connect the worker timeout signal to a lambda function in the main thread
QObject::connect(thread, &QThread::finished, worker, &TimerWorker::deleteLater); // connect the thread finished signal to the worker deleteLater slot
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); // connect the thread finished signal to the thread deleteLater slot
thread->start(); // start the thread
thread->wait(5000); // wait for 5 seconds
thread->quit(); // quit the thread
qDebug() << "Main thread finished";
return a.exec();
}
运行结果如下:
Main thread QThread(0x7ffedf9a1b80)
TimerWorker created in thread QThread(0x7ffedf9a1b80)
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:23"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:24"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:25"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:26"
Main thread received timeout signal
TimerWorker timeout in thread QThread(0x55c3c8f3c4c0)
Current time: "2022/12/30 10:15:27"
Main thread received timeout signal
Main thread finished
可以看到,TimerWorker对象最初是在主线程中创建的,但是在执行onTimeout()槽函数时,已经移动到了子线程中。每隔一秒,计时器就会触发一次timeout()信号,导致onTimeout()槽函数和主线程的lambda函数被执行。主线程在等待5秒后,退出了子线程,并删除了TimerWorker对象和子线程对象。这就是一个使用QThread的计时器触发的例子。
这里再举一个用QThread实现一个子线程调用摄像头的程序(例子3)
- 首先,使用Qt Designer创建一个UI界面,添加一个QLabel控件用来显示摄像头的画面,添加一个QPushButton控件用来启动和停止摄像头,添加一个QComboBox控件用来选择摄像头的索引号。
- 然后,定义一个MainWindow类,继承自QMainWindow,并使用Ui_MainWindow类来加载UI界面。
- 然后,在MainWindow类的构造函数中,创建一个QCamera对象,并将其setViewfinder()函数设置为QLabel控件的地址,这样就可以将摄像头的画面显示到QLabel控件上。
- 接着,在MainWindow类中,定义一个槽函数on_pushButton_clicked(),用来处理QPushButton控件的clicked()信号,根据QPushButton控件的文本判断是启动还是停止摄像头,并改变QPushButton控件的文本。
- 接着,在MainWindow类中,定义一个槽函数on_comboBox_currentIndexChanged(),用来处理QComboBox控件的currentIndexChanged()信号,根据QComboBox控件的当前索引值来设置QCamera对象的摄像头索引号,并重新启动摄像头。
代码如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCamera>
#include <QCameraInfo>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// create a camera object
camera = new QCamera(this);
// set the viewfinder to the label widget
camera->setViewfinder(ui->label);
// get the available cameras
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
// add the camera names to the combo box
foreach (const QCameraInfo &cameraInfo, cameras) {
ui->comboBox->addItem(cameraInfo.description());
}
}
MainWindow::~MainWindow()
{
delete ui;
}
// slot function for the push button
void MainWindow::on_pushButton_clicked()
{
// if the button text is "Start"
if (ui->pushButton->text() == "Start") {
// start the camera
camera->start();
// change the button text to "Stop"
ui->pushButton->setText("Stop");
}
// else if the button text is "Stop"
else if (ui->pushButton->text() == "Stop") {
// stop the camera
camera->stop();
// change the button text to "Start"
ui->pushButton->setText("Start");
}
}
// slot function for the combo box
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
// stop the camera
camera->stop();
// set the camera device index to the combo box current index
camera->setCaptureMode(QCamera::CaptureVideo);
camera->setCaptureMode(QCamera::CaptureStillImage);
camera->setCaptureMode(QCamera::CaptureViewfinder);
camera->setViewfinderSettings(QCameraViewfinderSettings());
camera->setViewfinder(ui->label);
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
foreach (const QCameraInfo &cameraInfo, cameras) {
if (cameraInfo.description() == ui->comboBox->currentText()) {
camera = new QCamera(cameraInfo);
break;
}
delete camera;
camera = new QCamera(cameras.at(index));
break;
// start the camera
camera->start();
// change the button text to "Stop"
ui->pushButton->setText("Stop");
}
最后,简单的总结一哈,QThread的用途!
使用QThread类对象来实现多线程的用途很多,比如:
- 可以在子线程中执行耗时的操作,如文件读写、网络请求、数据处理等,避免阻塞主线程的用户界面。
- 可以在子线程中创建和使用继承自QObject的类的对象,如QTimer、QTcpSocket、QUdpSocket、QProcess等,利用信号和槽机制来实现异步的事件处理。
- 可以在子线程中使用QEventLoop类来创建一个事件循环,以便在子线程中处理事件和信号。(Qt的事件循环是一种机制,用来接收和处理来自操作系统或程序框架的各种事件,如用户输入、窗口绘制、计时器触发等。)
- 可以在子线程中使用QThreadStorage类来为每个线程创建一个独立的存储空间,以存储线程相关的数据。
- 可以在子线程中使用QFuture、QFutureWatcher、QFutureSynchronizer等类来实现并行计算和异步结果的获取。
这些都是使用QThread类对象来实现多线程的一些常见用途。