实用指南:Qt-for-鸿蒙PC-多线程绘制开源鸿蒙开发实践

Qt for 鸿蒙PC 多线程绘制开源鸿蒙开发实践

项目概述

请添加图片描述

项目地址:https://gitcode.com/szkygc/HarmonyOs_PC-PGC/tree/main/Multithreading

本文档基于一个完整的 Multithreading 项目,详细介绍了如何在 HarmonyOS 平台上使用 Qt 实现多线程绘制功能。项目实现了两个独立的工作线程同时绘制圆形和方形,展示了 Qt 多线程编程、QML 与 C++ 集成、Canvas 绘制等技术在 HarmonyOS 平台上的实际应用。

✨ 主要功能

  • 多线程绘制:两个独立线程同时绘制圆形和方形
  • Worker 模式:使用 QThread + Worker 模式,符合 Qt 最佳实践
  • C++/QML 集成:C++ 自定义类型注册到 QML,实现无缝集成
  • Canvas 实时绘制:使用 Canvas 2D API 实时绘制线程生成的点
  • 线程安全通信:使用信号槽机制和 Qt::QueuedConnection 确保线程安全
  • 线程生命周期管理:完善的线程启动、停止和清理机制
  • 防止重复调用:使用标志位防止 doWork 被重复调用
  • 自适应窗口调整:窗口尺寸变化时自动调整绘制参数
  • 优雅停止机制:支持线程中断和资源清理

技术亮点

多线程架构

  • 使用 QThread + Worker 模式,符合 Qt 最佳实践
  • 工作对象(Worker)在独立线程中运行
  • 主对象(Thread)在主线程中供 QML 使用
  • 使用 Qt::QueuedConnection 确保线程安全

线程管理

  • 防止重复调用 doWork 的机制
  • 工作完成后自动停止线程
  • 完善的线程清理和资源释放

HarmonyOS 适配

  • 使用 qtmain() 作为入口函数
  • OpenGL ES 配置
  • QML 与 C++ 类型注册

️ 技术栈

  • 开发框架: Qt 5.15+ for HarmonyOS
  • 编程语言: C++ / QML / JavaScript
  • 多线程框架: QThread + Worker 模式
  • 图形渲染: Canvas 2D API
  • 界面框架: Qt Quick Controls 2
  • 构建工具: CMake
  • 目标平台: HarmonyOS (OpenHarmony) / PC

️ 项目架构

目录结构

Multithreading/
├── entry/src/main/
│   ├── cpp/
│   │   ├── main.cpp              # 应用入口(HarmonyOS适配)
│   │   ├── main.qml              # 主界面(Canvas绘制)
│   │   ├── CircleThread.h        # 圆形绘制线程头文件
│   │   ├── CircleThread.cpp      # 圆形绘制线程实现
│   │   ├── SquareThread.h        # 方形绘制线程头文件
│   │   ├── SquareThread.cpp      # 方形绘制线程实现
│   │   ├── CMakeLists.txt        # 构建配置
│   │   └── qml.qrc               # QML资源文件
│   ├── module.json5              # 模块配置
│   └── resources/                # 资源文件
└── image/
    └── 演示示例.gif              # 演示动图

组件层次结构

ApplicationWindow (main.qml)
├── ColumnLayout
│   ├── Text (标题)
│   ├── Rectangle (画布容器)
│   │   └── Canvas (绘制组件)
│   │       ├── onPaint (绘制函数)
│   │       └── 绘制圆形点(蓝色)
│   │       └── 绘制方形点(红色)
│   ├── RowLayout (控制按钮)
│   │   ├── Button (开始绘制)
│   │   ├── Button (停止绘制)
│   │   └── Button (清空画布)
│   └── Text (状态显示)
├── CircleThread (C++ 线程对象)
│   ├── CircleWorker (工作对象)
│   │   └── doWork() (在工作线程中执行)
│   └── 信号: circlePoint, finished
└── SquareThread (C++ 线程对象)
    ├── SquareWorker (工作对象)
    │   └── doWork() (在工作线程中执行)
    └── 信号: squarePoint, finished

多线程架构设计

主线程 (Main Thread)
├── ApplicationWindow (QML)
├── CircleThread (QObject)
│   ├── 管理 QThread
│   ├── 管理 CircleWorker
│   └── 信号槽连接 (Qt::QueuedConnection)
└── SquareThread (QObject)
    ├── 管理 QThread
    ├── 管理 SquareWorker
    └── 信号槽连接 (Qt::QueuedConnection)
工作线程 1 (Worker Thread 1)
└── CircleWorker
    └── doWork() - 计算圆形点
工作线程 2 (Worker Thread 2)
└── SquareWorker
    └── doWork() - 计算方形点

核心功能实现

1. HarmonyOS 入口函数:qtmain()

⚠️ 关键要点:HarmonyOS 真机上必须使用 qtmain() 而不是 main()

// ✅ 正确写法
extern "C" int qtmain(int argc, char **argv)
{
// Qt 应用作为共享库加载,生命周期由 HarmonyOS 管理
QGuiApplication app(argc, argv);
// 注册 C++ 类型到 QML
qmlRegisterType<CircleThread>("Multithreading", 1, 0, "CircleThread");
  qmlRegisterType<SquareThread>("Multithreading", 1, 0, "SquareThread");
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();  // ⚠️ 重要:必须调用 exec() 启动事件循环
    }
    // ❌ 错误写法(桌面应用方式)
    int main(int argc, char *argv[])
    {
    // 这种方式在 HarmonyOS 上会导致应用无法正常启动
    }

原因说明

  • HarmonyOS 将 Qt 应用作为共享库(.so)加载
  • 应用生命周期由 HarmonyOS 的 Ability 管理
  • qtmain() 是 HarmonyOS Qt 插件的标准入口点

2. OpenGL ES 表面格式配置

⚠️ 关键要点:必须在创建 QGuiApplication之前配置 QSurfaceFormat

// Step 1: 配置 OpenGL ES 表面格式(必须在创建应用之前!)
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QSurfaceFormat format;
// 设置 Alpha 通道(透明度)
format.setAlphaBufferSize(8);      // 8 位 Alpha 通道
// 设置颜色通道(RGBA 32 位真彩色)
format.setRedBufferSize(8);        // 8 位红色通道
format.setGreenBufferSize(8);      // 8 位绿色通道
format.setBlueBufferSize(8);       // 8 位蓝色通道
// 设置深度和模板缓冲区
format.setDepthBufferSize(24);     // 24 位深度缓冲
format.setStencilBufferSize(8);    // 8 位模板缓冲
// 指定渲染类型为 OpenGL ES(HarmonyOS要求)
format.setRenderableType(QSurfaceFormat::OpenGLES);
// 指定 OpenGL ES 版本为 2.0(推荐,兼容性更好)
format.setVersion(2, 0);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setSamples(0);
// ⚠️ 关键:必须在创建 QGuiApplication 之前设置默认格式!
QSurfaceFormat::setDefaultFormat(format);
// Step 2: 创建 Qt 应用实例(必须在设置格式之后)
QGuiApplication app(argc, argv);

3. Qt 多线程架构:Worker 模式

核心思想:将工作对象(Worker)移动到工作线程,主对象(Thread)在主线程中供 QML 使用。

3.1 Worker 类设计
// 工作对象:实际在工作线程中运行
class CircleWorker : public QObject
{
Q_OBJECT
public:
explicit CircleWorker(QObject *parent = nullptr);
void requestStop() { m_shouldStop = true; }
public slots:
void doWork(const QPoint &center, int radius);
signals:
void pointGenerated(const QPoint &pt);
void workFinished();
private:
bool m_shouldStop;
};

关键点

  • CircleWorker 继承自 QObject,可以使用信号槽
  • doWork() 是槽函数,在工作线程中执行
  • 通过信号 pointGeneratedworkFinished 与主线程通信
  • m_shouldStop 标志用于优雅停止
3.2 Thread 类设计
// 主对象:始终在主线程,供QML使用
class CircleThread : public QObject
{
Q_OBJECT
Q_PROPERTY(QPoint center READ center WRITE setCenter NOTIFY centerChanged)
Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
public:
explicit CircleThread(QObject *parent = nullptr);
~CircleThread();
QPoint center() const { return m_center; }
void setCenter(const QPoint &center);
int radius() const { return m_radius; }
void setRadius(int radius);
bool running() const { return m_running; }
public slots:
void start();
void stop();
signals:
void circlePoint(const QPoint &pt);
void centerChanged();
void radiusChanged();
void runningChanged();
void finished();
private slots:
void onWorkerFinished();
void onPointGenerated(const QPoint &pt);
void onThreadStarted();
private:
QPoint m_center;
int m_radius;
QThread *m_thread;
CircleWorker *m_worker;
bool m_running;
bool m_workStarted;  // 防止重复调用 doWork
};

关键点

  • CircleThread 继承自 QObject,使用 Q_PROPERTY 暴露属性给 QML
  • 管理 QThreadCircleWorker 的生命周期
  • 使用 Qt::QueuedConnection 连接信号槽,确保线程安全

4. 线程初始化和 Worker 移动

⚠️ 关键要点:Worker 对象必须移动到工作线程!

CircleThread::CircleThread(QObject *parent)
: QObject(parent)
, m_center(0, 0)
, m_radius(0)
, m_thread(nullptr)
, m_worker(nullptr)
, m_running(false)
, m_workStarted(false)
{
// 创建工作线程和工作对象
m_thread = new QThread(this);
m_worker = new CircleWorker();
// ⚠️ 关键:将工作对象移动到工作线程
// 这一步非常重要!只有移动到线程后,Worker 的槽函数才会在工作线程中执行
m_worker->moveToThread(m_thread);
// 连接信号槽:工作对象 -> 主对象
// ⚠️ 使用 Qt::QueuedConnection 确保线程安全
connect(m_worker, &CircleWorker::pointGenerated,
this, &CircleThread::onPointGenerated,
Qt::QueuedConnection);
connect(m_worker, &CircleWorker::workFinished,
this, &CircleThread::onWorkerFinished,
Qt::QueuedConnection);
// 连接线程信号:线程启动后开始工作
connect(m_thread, &QThread::started,
this, &CircleThread::onThreadStarted);
// 线程结束时清理工作对象
connect(m_thread, &QThread::finished,
m_worker, &QObject::deleteLater);
}

为什么使用 Qt::QueuedConnection

  • Qt::QueuedConnection 确保信号在接收对象的线程中执行
  • Worker 在工作线程中发送信号,主对象在主线程中接收
  • 这样可以安全地更新 UI(QML 必须在主线程中更新)

5. 线程启动和防止重复调用

问题QThread::started 信号和备用机制可能同时触发,导致 doWork 被调用两次。

解决方案:使用 m_workStarted 标志防止重复调用。

void CircleThread::start()
{
// 检查是否已经在运行
if (m_running || !m_thread) {
return;
}
// 如果线程已经在运行,先停止并等待完成
if (m_thread->isRunning()) {
stop();
if (!m_thread->wait(2000)) {
qWarning() << "CircleThread: 线程未在2秒内停止";
return;
}
}
// 重新创建工作对象(如果已被删除)
if (!m_worker) {
m_worker = new CircleWorker();
m_worker->moveToThread(m_thread);
// 重新连接信号槽...
}
// 重置标志
m_running = true;
m_workStarted = false;  // ⚠️ 重置工作开始标志
emit runningChanged();
// 启动线程
m_thread->start();
// 备用机制:如果信号没有触发,延迟调用 onThreadStarted
QTimer::singleShot(50, this, [this]() {
// ⚠️ 检查 m_workStarted,防止重复调用
if (m_running && !m_workStarted && m_worker && m_thread->isRunning()) {
qDebug() << "CircleThread: 备用机制 - 直接调用 onThreadStarted";
onThreadStarted();
}
});
}
void CircleThread::onThreadStarted()
{
// ⚠️ 防止重复调用
if (m_workStarted) {
qDebug() << "CircleThread: onThreadStarted 已被调用过,跳过重复调用";
return;
}
// 检查 worker 是否存在
if (!m_worker) {
qWarning() << "CircleThread: m_worker 为 nullptr,无法调用 doWork";
return;
}
// ⚠️ 标记工作已开始,防止重复调用
m_workStarted = true;
// 使用 QMetaObject::invokeMethod 在工作线程中调用 doWork
bool success = QMetaObject::invokeMethod(m_worker, "doWork",
Qt::QueuedConnection,
Q_ARG(QPoint, m_center),
Q_ARG(int, m_radius));
if (!success) {
qWarning() << "CircleThread: invokeMethod 失败";
m_workStarted = false;  // 如果调用失败,重置标志
}
}

关键点

  • m_workStarted 标志确保 doWork 只被调用一次
  • 备用机制中也检查该标志,避免重复调用
  • 如果 invokeMethod 失败,重置标志以便重试

6. Worker 工作实现

圆形绘制:使用参数方程计算圆周上的点。

void CircleWorker::doWork(const QPoint &center, int radius)
{
qDebug() << "CircleWorker::doWork 开始 - center:" << center << "radius:" << radius;
m_shouldStop = false;
// 检查参数有效性
if (radius <= 0) {
qWarning() << "CircleWorker: radius 无效:" << radius;
emit workFinished();
return;
}
// 使用固定点数绘制圆形(360个点,每个点1度)
const int totalPoints = 360;
double radianIncrement = 2.0 * M_PI / totalPoints;
int pointCount = 0;
// 遍历所有点
for (int i = 0; i < totalPoints && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++i) {
// 计算当前角度
double circleAngle = i * radianIncrement;
// 计算新点的位置(参数方程)
int centerX = center.x();
int centerY = center.y();
int x = centerX + static_cast<int>(radius * cos(circleAngle));
  int y = centerY + static_cast<int>(radius * sin(circleAngle));
    // 发送新点的位置(通过信号发送到主线程)
    emit pointGenerated(QPoint(x, y));
    pointCount++;
    QThread::msleep(62);  // 控制绘制速度
    }
    qDebug() << "CircleWorker: 画圆完成,共生成" << pointCount << "个点";
    emit workFinished();
    }

方形绘制:沿着四条边依次绘制点。

void SquareWorker::doWork(const QPoint &topLeft, int sideLength)
{
qDebug() << "SquareWorker::doWork 开始 - topLeft:" << topLeft << "sideLength:" << sideLength;
m_shouldStop = false;
// 检查参数有效性
if (sideLength <= 0) {
qWarning() << "SquareWorker: sideLength 无效:" << sideLength;
emit workFinished();
return;
}
// 方形的四个顶点
QPoint points[4] = {
topLeft,
QPoint(topLeft.x() + sideLength, topLeft.y()),
QPoint(topLeft.x() + sideLength, topLeft.y() + sideLength),
QPoint(topLeft.x(), topLeft.y() + sideLength)
};
int pointCount = 0;
// 沿着每条边绘制点
for (int i = 0; i < 4 && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++i) {
QPoint start = points[i];
QPoint end = points[(i + 1) % 4];
// 使用浮点数计算避免整数除法精度问题
double dx = static_cast<double>(end.x() - start.x()) / sideLength;
  double dy = static_cast<double>(end.y() - start.y()) / sideLength;
    for (int j = 0; j <= sideLength && !m_shouldStop && !QThread::currentThread()->isInterruptionRequested(); ++j) {
    int x = start.x() + static_cast<int>(dx * j);
      int y = start.y() + static_cast<int>(dy * j);
        emit pointGenerated(QPoint(x, y));
        pointCount++;
        QThread::msleep(5);  // 控制绘制速度
        }
        }
        qDebug() << "SquareWorker: 画方完成,共生成" << pointCount << "个点";
        emit workFinished();
        }

关键点

  • 检查 m_shouldStopQThread::currentThread()->isInterruptionRequested() 以支持优雅停止
  • 使用 emit pointGenerated() 将点发送到主线程
  • 工作完成后发送 workFinished() 信号

7. 线程停止和清理

⚠️ 关键要点:工作完成后必须停止线程!

void CircleThread::onWorkerFinished()
{
qDebug() << "CircleThread: onWorkerFinished 被调用 - 当前 m_running:" << m_running;
// ⚠️ 停止线程
if (m_thread && m_thread->isRunning()) {
qDebug() << "CircleThread: 工作完成,停止线程";
m_thread->quit();
if (!m_thread->wait(1000)) {
qWarning() << "CircleThread: 线程未在1秒内停止,强制终止";
m_thread->terminate();
m_thread->wait(500);
}
}
// 更新状态
m_running = false;
m_workStarted = false;  // 重置工作开始标志
emit runningChanged();
emit finished();
// 标记worker为null,因为会被deleteLater删除
m_worker = nullptr;
qDebug() << "CircleThread: 工作完成";
}
void CircleThread::stop()
{
if (!m_thread) {
return;
}
// 无论 m_running 是什么,只要线程在运行就停止它
if (m_thread->isRunning()) {
qDebug() << "CircleThread: 停止线程 - m_running:" << m_running;
// 通知工作对象停止
if (m_worker) {
QMetaObject::invokeMethod(m_worker, "requestStop", Qt::QueuedConnection);
}
m_thread->requestInterruption();
m_thread->quit();
if (!m_thread->wait(1000)) {
qWarning() << "CircleThread: 线程未在1秒内停止,强制终止";
m_thread->terminate();
m_thread->wait(500);
}
}
// 更新运行状态
if (m_running) {
m_running = false;
m_workStarted = false;  // 重置工作开始标志
emit runningChanged();
}
qDebug() << "CircleThread: 停止画圆线程完成 - isRunning:" << (m_thread ? m_thread->isRunning() : false);
  }

关键点

  • onWorkerFinished() 中自动停止线程
  • 使用 quit() 优雅停止,如果超时则使用 terminate() 强制终止
  • 重置所有状态标志
  • Worker 对象通过 deleteLater 自动清理

8. QML 中使用 C++ 类型

注册类型

// 在 main.cpp 中注册
qmlRegisterType<CircleThread>("Multithreading", 1, 0, "CircleThread");
  qmlRegisterType<SquareThread>("Multithreading", 1, 0, "SquareThread");

QML 中使用

import Multithreading 1.0
ApplicationWindow {
    id: root
    // 圆形点集合
    property var circlePoints: []
    // 方形点集合
    property var squarePoints: []
    // 画圆线程
    CircleThread {
        id: circleThread
        onCirclePoint: function(pt) {
            root.addCirclePoint(pt);
        }
        onRunningChanged: {
            console.log("Multithreading: 圆形线程 running 状态变化 - running:", running);
        }
        onFinished: {
            console.log("Multithreading: 画圆线程完成 - running:", running);
        }
    }
    // 画方线程
    SquareThread {
        id: squareThread
        onSquarePoint: function(pt) {
            root.addSquarePoint(pt);
        }
        onRunningChanged: {
            console.log("Multithreading: 方形线程 running 状态变化 - running:", running);
        }
        onFinished: {
            console.log("Multithreading: 画方线程完成 - running:", running);
        }
    }
    // 添加圆形点
    function addCirclePoint(pt) {
        if (!pt) {
            return;
        }
        var newPoints = circlePoints.slice();
        newPoints.push({"x": pt.x, "y": pt.y});
        circlePoints = newPoints;
        // 触发 Canvas 重绘
        if (canvas.width > 0 && canvas.height > 0) {
            canvas.requestPaint();
        }
    }
    // 添加方形点
    function addSquarePoint(pt) {
        if (!pt) {
            return;
        }
        var newPoints = squarePoints.slice();
        newPoints.push({"x": pt.x, "y": pt.y});
        squarePoints = newPoints;
        // 触发 Canvas 重绘
        if (canvas.width > 0 && canvas.height > 0) {
            canvas.requestPaint();
        }
    }
    // 启动绘制
    Button {
        text: "开始绘制"
        enabled: !circleThread.running && !squareThread.running
        onClicked: {
            // 清空之前的点
            circlePoints = [];
            squarePoints = [];
            canvas.requestPaint();
            // 更新绘制参数
            updateDrawingParameters();
            // 启动线程
            Qt.callLater(function() {
                circleThread.start();
                squareThread.start();
            });
        }
    }
}

关键点

  • 使用 import Multithreading 1.0 导入自定义类型
  • 通过 onCirclePoint 等信号处理器接收数据
  • 使用 running 属性控制按钮状态
  • 在 QML 中直接调用 C++ 的 start()stop() 方法

9. Canvas 绘制实现

Canvas 绘制函数

Canvas {
    id: canvas
    anchors.fill: parent
    onPaint: {
        var ctx = getContext("2d");
        if (!ctx) {
            return;
        }
        // 填充白色背景
        ctx.fillStyle = "white";
        ctx.fillRect(0, 0, width, height);
        // 绘制圆形点(蓝色)
        ctx.strokeStyle = "blue";
        ctx.lineWidth = 2;
        if (circlePoints.length > 0) {
            ctx.beginPath();
            var pathStarted = false;
            for (var i = 0; i < circlePoints.length; i++) {
                var pt = circlePoints[i];
                // 检查坐标是否在 Canvas 范围内
                if (pt.x >= 0 && pt.x <= width && pt.y >= 0 && pt.y <= height) {
                    if (!pathStarted) {
                        ctx.moveTo(pt.x, pt.y);
                        pathStarted = true;
                    } else {
                        ctx.lineTo(pt.x, pt.y);
                    }
                }
            }
            if (pathStarted) {
                ctx.stroke();
            }
        }
        // 绘制方形点(红色)
        ctx.strokeStyle = "red";
        ctx.lineWidth = 2;
        if (squarePoints.length > 0) {
            ctx.beginPath();
            var squarePathStarted = false;
            for (var j = 0; j < squarePoints.length; j++) {
                var sqPt = squarePoints[j];
                if (sqPt.x >= 0 && sqPt.x <= width && sqPt.y >= 0 && sqPt.y <= height) {
                    if (!squarePathStarted) {
                        ctx.moveTo(sqPt.x, sqPt.y);
                        squarePathStarted = true;
                    } else {
                        ctx.lineTo(sqPt.x, sqPt.y);
                    }
                }
            }
            if (squarePathStarted) {
                ctx.stroke();
            }
        }
    }
}

关键点

  • 使用 onPaint 处理绘制逻辑
  • 检查坐标是否在 Canvas 范围内
  • 使用 beginPath()moveTo()lineTo()stroke() 绘制路径
  • 每次收到新点时调用 canvas.requestPaint() 触发重绘

⚠️ 常见陷阱和最佳实践

避免的做法

  1. 在主线程中执行耗时操作

    // ❌ 错误:在主线程中执行耗时操作
    void CircleThread::start() {
    // 这会阻塞主线程,导致 UI 卡顿
    for (int i = 0; i < 360; ++i) {
    // 计算点...
    }
    }
  2. 直接访问 Worker 对象

    // ❌ 错误:直接调用 Worker 的方法
    m_worker->doWork(center, radius);  // 这会在主线程中执行!
  3. 忘记移动 Worker 到线程

    // ❌ 错误:忘记 moveToThread
    m_worker = new CircleWorker();
    // 没有 moveToThread,Worker 仍在主线程中
  4. 使用 AutoConnection 连接信号槽

    // ❌ 错误:可能使用 DirectConnection(如果对象在同一线程)
    connect(m_worker, &CircleWorker::pointGenerated,
    this, &CircleThread::onPointGenerated);  // 默认 AutoConnection
  5. 不处理线程停止

    // ❌ 错误:工作完成后不停止线程
    void CircleThread::onWorkerFinished() {
    m_running = false;
    // 忘记停止线程,导致资源浪费
    }

推荐的做法

  1. 使用 Worker 模式

    // ✅ 正确:Worker 在工作线程中执行
    m_worker->moveToThread(m_thread);
    QMetaObject::invokeMethod(m_worker, "doWork", Qt::QueuedConnection, ...);
  2. 使用 QueuedConnection

    // ✅ 正确:确保线程安全
    connect(m_worker, &CircleWorker::pointGenerated,
    this, &CircleThread::onPointGenerated,
    Qt::QueuedConnection);
  3. 防止重复调用

    // ✅ 正确:使用标志防止重复调用
    bool m_workStarted;
    void CircleThread::onThreadStarted() {
    if (m_workStarted) {
    return;  // 已调用过,跳过
    }
    m_workStarted = true;
    // 调用 doWork...
    }
  4. 工作完成后停止线程

    // ✅ 正确:工作完成后停止线程
    void CircleThread::onWorkerFinished() {
    if (m_thread && m_thread->isRunning()) {
    m_thread->quit();
    m_thread->wait(1000);
    }
    m_running = false;
    m_workStarted = false;
    }
  5. 优雅停止工作

    // ✅ 正确:检查停止标志
    void CircleWorker::doWork(...) {
    for (int i = 0; i < totalPoints && !m_shouldStop &&
    !QThread::currentThread()->isInterruptionRequested(); ++i) {
    // 工作...
    }
    }

完整代码示例

CircleThread.h

#ifndef CIRCLETHREAD_H
#define CIRCLETHREAD_H
#include <QObject>
  #include <QThread>
    #include <QPoint>
      #include <QDebug>
        #include <cmath>
          // 工作对象:实际在工作线程中运行
          class CircleWorker : public QObject
          {
          Q_OBJECT
          public:
          explicit CircleWorker(QObject *parent = nullptr);
          void requestStop() { m_shouldStop = true; }
          public slots:
          void doWork(const QPoint &center, int radius);
          signals:
          void pointGenerated(const QPoint &pt);
          void workFinished();
          private:
          bool m_shouldStop;
          };
          // 主对象:始终在主线程,供QML使用
          class CircleThread : public QObject
          {
          Q_OBJECT
          Q_PROPERTY(QPoint center READ center WRITE setCenter NOTIFY centerChanged)
          Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged)
          Q_PROPERTY(bool running READ running NOTIFY runningChanged)
          public:
          explicit CircleThread(QObject *parent = nullptr);
          ~CircleThread();
          QPoint center() const { return m_center; }
          void setCenter(const QPoint &center);
          int radius() const { return m_radius; }
          void setRadius(int radius);
          bool running() const { return m_running; }
          public slots:
          void start();
          void stop();
          signals:
          void circlePoint(const QPoint &pt);
          void centerChanged();
          void radiusChanged();
          void runningChanged();
          void finished();
          private slots:
          void onWorkerFinished();
          void onPointGenerated(const QPoint &pt);
          void onThreadStarted();
          private:
          QPoint m_center;
          int m_radius;
          QThread *m_thread;
          CircleWorker *m_worker;
          bool m_running;
          bool m_workStarted;  // 防止重复调用 doWork
          };
          #endif // CIRCLETHREAD_H

CircleThread.cpp(关键部分)

#include "CircleThread.h"
#include <QTimer>
  CircleWorker::CircleWorker(QObject *parent)
  : QObject(parent)
  , m_shouldStop(false)
  {
  }
  void CircleWorker::doWork(const QPoint &center, int radius)
  {
  m_shouldStop = false;
  if (radius <= 0) {
  emit workFinished();
  return;
  }
  const int totalPoints = 360;
  double radianIncrement = 2.0 * M_PI / totalPoints;
  for (int i = 0; i < totalPoints && !m_shouldStop &&
  !QThread::currentThread()->isInterruptionRequested(); ++i) {
  double circleAngle = i * radianIncrement;
  int x = center.x() + static_cast<int>(radius * cos(circleAngle));
    int y = center.y() + static_cast<int>(radius * sin(circleAngle));
      emit pointGenerated(QPoint(x, y));
      QThread::msleep(62);
      }
      emit workFinished();
      }
      CircleThread::CircleThread(QObject *parent)
      : QObject(parent)
      , m_center(0, 0)
      , m_radius(0)
      , m_thread(nullptr)
      , m_worker(nullptr)
      , m_running(false)
      , m_workStarted(false)
      {
      m_thread = new QThread(this);
      m_worker = new CircleWorker();
      m_worker->moveToThread(m_thread);
      connect(m_worker, &CircleWorker::pointGenerated,
      this, &CircleThread::onPointGenerated, Qt::QueuedConnection);
      connect(m_worker, &CircleWorker::workFinished,
      this, &CircleThread::onWorkerFinished, Qt::QueuedConnection);
      connect(m_thread, &QThread::started,
      this, &CircleThread::onThreadStarted);
      connect(m_thread, &QThread::finished,
      m_worker, &QObject::deleteLater);
      }
      void CircleThread::onThreadStarted()
      {
      if (m_workStarted) {
      return;  // 防止重复调用
      }
      if (!m_worker) {
      return;
      }
      m_workStarted = true;
      QMetaObject::invokeMethod(m_worker, "doWork", Qt::QueuedConnection,
      Q_ARG(QPoint, m_center),
      Q_ARG(int, m_radius));
      }
      void CircleThread::onWorkerFinished()
      {
      if (m_thread && m_thread->isRunning()) {
      m_thread->quit();
      m_thread->wait(1000);
      }
      m_running = false;
      m_workStarted = false;
      emit runningChanged();
      emit finished();
      m_worker = nullptr;
      }
      void CircleThread::stop()
      {
      if (!m_thread) {
      return;
      }
      if (m_thread->isRunning()) {
      if (m_worker) {
      QMetaObject::invokeMethod(m_worker, "requestStop", Qt::QueuedConnection);
      }
      m_thread->requestInterruption();
      m_thread->quit();
      m_thread->wait(1000);
      }
      if (m_running) {
      m_running = false;
      m_workStarted = false;
      emit runningChanged();
      }
      }

相关资源


总结

本文详细介绍了在 HarmonyOS 平台上使用 Qt 实现多线程绘制的完整方案,包括:

  1. 多线程架构:使用 Worker 模式,将工作对象移动到独立线程
  2. 线程安全:使用 Qt::QueuedConnection 确保线程间通信安全
  3. 防止重复调用:使用标志位防止 doWork 被重复调用
  4. 线程管理:工作完成后自动停止线程,完善的清理机制
  5. QML 集成:C++ 类型注册到 QML,实现无缝集成
  6. Canvas 绘制:实时绘制线程生成的点,实现动态效果

通过这个项目,我们可以学习到 Qt 多线程编程的最佳实践,以及如何在 HarmonyOS 平台上正确使用 Qt 进行应用开发。

posted @ 2025-12-16 16:08  clnchanpin  阅读(45)  评论(0)    收藏  举报