以线程安全的方式调用`QQuickPaintedItem::updateImage(const QImage& image)`(无 QThread)

Calling `QQuickPaintedItem::updateImage(const QImage& image)` in a thread safe way (no QThread)

我正在阅读有关在 C++ 中创建自定义 QQuickPaintedItem 的答案

并且我想编写不依赖于 Qt 的代码。也就是说,我不想依赖QThread。我只想从 std::thread 调用 CameraView::updateImage(const QImage& image),而不是 q QThread。可能吗?

我想简单地创建一个线程,将 CameraView 实例传递给它,然后从线程调用它。但是,这不是线程安全的。

如何以线程安全的方式调用 CameraView::updateImage(const QImage& image)

当然,您可以在代码中使用 C++ 标准库 类。例如,使用 std::mutex 更新图像如下:

void CameraView::updateImage(const QImage& image) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere
    // ... the rest of the code as before ...
}

您也可以保留 CameraView 代码不变,并从外部代码调用 updateImage 函数。只需确保在所有线程中使用相同的互斥体更新图像即可:

void functionInAnotherThread(CameraView& cameraView) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere

    // ... the rest of the code ...

    cameraView.updateImage(image);

    // ... the rest of the code ...
}

让我们尝试实现这种方法。让我们稍微更改一下头文件:

class CameraView : public QQuickPaintedItem {
    Q_OBJECT
    Q_DISABLE_COPY(CameraView)

public:
    CameraView(QQuickItem* parent = nullptr);

public slots:
    void updateImage(const QImage&);
    void scheduleUpdate();

protected:
    QImage image_;
};

现在,编写方法的定义:

void CameraView::updateImage(const QImage& image) {
    image_ = image.copy(); // Does deep copy of image data.
}

void CameraView::scheduleUpdate() {
    update();
}

void CameraView::paint(QPainter* painter) {
    painter->drawImage(this->boundingRect(), image_);
}

最后我们写更新图片和调度重绘的函数:

void functionInAnotherThread(CameraView& cameraView) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere

    cameraView.updateImage(image);

    lock.unlock(); // Unlock the mutex as we have already updated the image.

    QMetaObject::invokeMethod(&cameraView, "scheduleUpdate",
        Qt::QueuedConnection); // Call slot through the queued connection.
}

使用Qt::QueuedConnection,当控制returns到接收者线程的事件循环时调用槽。插槽在接收方的线程中执行。因此,我们可以安排从另一个线程重绘小部件。如果这不起作用,请尝试使用其他类型的连接。