qt中的多线程
阅读数:352 评论数:0
跳转到新版页面分类
C/C++
正文
一、基本概念
在 Qt 中,QThread
类用于表示一个线程。你可以通过继承 QThread
或者将对象移动到线程来实现多线程编程。推荐的方法是将对象移动到线程,因为它更符合 Qt 的信号槽机制。
二、创建和启动线程
1、继承QThread
这种方法通过继承 QThread
并重写 run
方法来实现线程任务。
#include <QThread>
#include <QDebug>
class MyThread : public QThread {
Q_OBJECT
protected:
void run() override {
for (int i = 0; i < 5; ++i) {
qDebug() << "Thread running" << i;
QThread::sleep(1);
}
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyThread thread;
thread.start();
thread.wait(); // 等待线程结束
return app.exec();
}
2、将对象移动到线程
这种方法更推荐,因为它更符合 Qt 的信号槽机制,并且更容易管理对象的生命周期。
#include <QThread>
#include <QObject>
#include <QDebug>
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
for (int i = 0; i < 5; ++i) {
qDebug() << "Worker running" << i;
QThread::sleep(1);
}
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QThread workerThread;
Worker worker;
worker.moveToThread(&workerThread);
QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&workerThread, &QThread::finished, &worker, &QObject::deleteLater);
workerThread.start();
workerThread.wait(); // 等待线程结束
return app.exec();
}
三、线程间通信
Qt 的信号槽机制可以跨线程工作。你可以使用信号槽在线程之间传递数据。
#include <QThread>
#include <QObject>
#include <QDebug>
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
for (int i = 0; i < 5; ++i) {
qDebug() << "Worker running" << i;
emit progress(i);
QThread::sleep(1);
}
emit finished();
}
signals:
void progress(int value);
void finished();
};
class Controller : public QObject {
Q_OBJECT
public slots:
void onProgress(int value) {
qDebug() << "Progress:" << value;
}
void onFinished() {
qDebug() << "Task finished!";
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QThread workerThread;
Worker worker;
Controller controller;
worker.moveToThread(&workerThread);
QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&worker, &Worker::progress, &controller, &Controller::onProgress);
QObject::connect(&worker, &Worker::finished, &controller, &Controller::onFinished);
QObject::connect(&worker, &Worker::finished, &workerThread, &QThread::quit);
QObject::connect(&workerThread, &QThread::finished, &worker, &QObject::deleteLater);
workerThread.start();
return app.exec();
}
1、信号与槽的连接类型
qt支持以下几种连接类型:
(1)自动连接(Qt::AutoConnection,默认)
根据信号发送者和接收者是否属于同一个线程,自动选择连接方式:
- 同线程:采用直接连接(
Qt::DirectConnection
)。 - 跨线程:采用队列连接(
Qt::QueuedConnection
)。
(2)直接连接(Qt::DirectConnection)
- 信号直接调用槽函数。
- 槽函数在信号发送者的线程中执行。
- 适用于同一线程中对象间的通信,或者线程间信号发射者明确知道槽函数线程上下文的情况。
(3)队列连接(Qt::QueuedConnection)
- 信号被放入接收者所在线程的事件队列。
- 槽函数在接收者对象所属线程中执行。
- 需要确保接收者线程有一个运行的事件循环。
(4)阻塞队列连接(Qt::BlockingQueuedConnection)
- 类似队列连接,但发送信号的线程会阻塞,直到槽函数执行完成。
- 发送线程必须与接收线程不同,否则会导致死锁。
- 使用场景:跨线程同步操作。
(5)唯一连接(Qt::UniqueConnection)
- 确保同一信号与槽的连接只存在一次。
- 通常与其他连接类型组合使用,例如:
Qt::AutoConnection | Qt::UniqueConnection
。
2、强制让槽函数在主线程中执行
(1)QMetaObject::invokeMethod
QMetaObject::invokeMethod
可以强制在指定的线程中执行函数。要确保在主线程中执行,可以如下操作:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QObject>
class Worker : public QObject {
Q_OBJECT
public:
void doWork() {
emit workDone();
}
signals:
void workDone();
};
class MainObject : public QObject {
Q_OBJECT
public slots:
void onWorkDone() {
qDebug() << "Slot executed in thread:" << QThread::currentThread();
}
void invokeSlotInMainThread() {
// 强制在主线程执行槽函数
QMetaObject::invokeMethod(this, "onWorkDone", Qt::QueuedConnection);
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
Worker worker;
MainObject mainObject;
// 验证主线程
qDebug() << "Main thread:" << QThread::currentThread();
// 将信号连接到一个强制调用的方法
QObject::connect(&worker, &Worker::workDone, &mainObject, &MainObject::invokeSlotInMainThread, Qt::QueuedConnection);
// 模拟在工作线程中发射信号
QThread workerThread;
worker.moveToThread(&workerThread);
QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&workerThread, &QThread::finished, &workerThread, &QThread::deleteLater);
workerThread.start(); // 启动线程
return app.exec();
}
(2)通过QCoreApplication::postEvent自定义事件
可以使用 QCoreApplication::postEvent
将任务封装成事件,发送到主线程的事件循环中执行。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QObject>
#include <QEvent>
class CustomEvent : public QEvent {
public:
static constexpr QEvent::Type EventType = static_cast<QEvent::Type>(QEvent::User + 1);
CustomEvent(std::function<void()> func) : QEvent(EventType), m_func(std::move(func)) {}
void execute() { m_func(); }
private:
std::function<void()> m_func;
};
class MainObject : public QObject {
Q_OBJECT
protected:
void customEvent(QEvent *event) override {
if (event->type() == CustomEvent::EventType) {
auto *customEvent = static_cast<CustomEvent *>(event);
customEvent->execute();
}
}
public slots:
void handleTask() {
qDebug() << "Task executed in thread:" << QThread::currentThread();
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
MainObject mainObject;
// 模拟从子线程发送任务
QThread workerThread;
QObject::connect(&workerThread, &QThread::started, [&]() {
QCoreApplication::postEvent(&mainObject, new CustomEvent([&mainObject]() {
mainObject.handleTask();
}));
});
QObject::connect(&workerThread, &QThread::finished, &workerThread, &QThread::deleteLater);
workerThread.start();
return app.exec();
}
(3)使用全局单例队列和主线程定时器
通过全局任务队列,利用主线程定时器定期拉取任务执行。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QObject>
#include <QTimer>
#include <queue>
#include <mutex>
#include <functional>
std::queue<std::function<void()>> taskQueue;
std::mutex queueMutex;
void postTaskToMainThread(std::function<void()> task) {
std::lock_guard<std::mutex> lock(queueMutex);
taskQueue.push(task);
}
void processMainThreadTasks() {
std::lock_guard<std::mutex> lock(queueMutex);
while (!taskQueue.empty()) {
auto task = taskQueue.front();
taskQueue.pop();
task();
}
}
class Worker : public QObject {
Q_OBJECT
public:
void doWork() {
postTaskToMainThread([]() {
qDebug() << "Task executed in thread:" << QThread::currentThread();
});
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, processMainThreadTasks);
timer.start(100); // 定期处理任务
Worker worker;
QThread workerThread;
worker.moveToThread(&workerThread);
QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&workerThread, &QThread::finished, &workerThread, &QThread::deleteLater);
workerThread.start();
return app.exec();
}
四、线程安全
在多线程编程中,确保线程安全非常重要。Qt 提供了 QMutex
、QReadWriteLock
、QSemaphore
等类来实现线程同步。
1、使用QMutex保护共享数据
#include <QThread>
#include <QMutex>
#include <QDebug>
class Counter {
public:
void increment() {
QMutexLocker locker(&mutex);
++value;
qDebug() << "Counter value:" << value;
}
private:
int value = 0;
QMutex mutex;
};
class Worker : public QThread {
Q_OBJECT
public:
Worker(Counter *counter) : counter(counter) {}
protected:
void run() override {
for (int i = 0; i < 5; ++i) {
counter->increment();
QThread::sleep(1);
}
}
private:
Counter *counter;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Counter counter;
Worker worker1(&counter);
Worker worker2(&counter);
worker1.start();
worker2.start();
worker1.wait();
worker2.wait();
return app.exec();
}
五、线程池
Qt 提供了 QThreadPool
和 QRunnable
来管理和执行多个线程任务。线程池可以重复使用线程,从而减少线程创建和销毁的开销。
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>
class Task : public QRunnable {
public:
void run() override {
for (int i = 0; i < 5; ++i) {
qDebug() << "Task running" << i;
QThread::sleep(1);
}
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QThreadPool pool;
Task *task1 = new Task();
Task *task2 = new Task();
pool.start(task1);
pool.start(task2);
pool.waitForDone();
return app.exec();
}
六、QThread::sleep
QThread::sleep()
是一个静态方法,用于让当前线程暂停执行一段时间。它有几个不同的版本,分别用于秒、毫秒和微秒级别的暂停。
1、秒级暂停
QThread::sleep(2); // 暂停当前线程2秒
2、毫秒级暂停
QThread::msleep(500); // 暂停当前线程500毫秒
3、微秒级暂停
QThread::usleep(1000); // 暂停当前线程1000微秒(1毫秒)
相关推荐
在Qt应用程序中定制桌面图标,通常涉及到两个方面:
应用程序图标:这是应用程序在操作系统中显示的图标,例如在Windows的任务栏或MacOS的Dock中。
桌面快捷方式图标:这是用户可以双击
一、基本使用
1、从Qt官方网站下载并安装Qt installer Framework
https://download.qt.io/official_releases/qt-installer-fr