简体   繁体   English

发出 Qt 信号的线程安全

[英]Thread safety of emitting Qt signals

I'm using QtConcurrent::run (I know other APIs of QtConcurrent has built in support for progress reporting but I can't use them for other reasons).我正在使用QtConcurrent::run (我知道 QtConcurrent 的其他 API 内置了对进度报告的支持,但由于其他原因我不能使用它们)。 to run an operation inside a different than the main GUI thread.在与主 GUI 线程不同的内部运行操作。 I also need this operation to notify the GUI thread of the progress made.我还需要这个操作来通知 GUI 线程取得的进展。 So what I did is that created a separate function for the operation I want which accepts a callback that carries the information about the progress of the operation.所以我所做的是为我想要的操作创建一个单独的 function,它接受一个回调,该回调包含有关操作进度的信息。 This callback then calls the signal on a QObject living in the main thread.然后,此回调调用位于主线程中的 QObject 上的信号。

Here is a full working example that shows my structure:这是一个完整的工作示例,显示了我的结构:

    #include <QCoreApplication>
    #include <QObject>
    #include <QThread>
    #include <QtConcurrent/QtConcurrent>
    
    
    namespace Operations {
    
    template<typename Callback>
    void longOperation(Callback progressCallback)
    {
        qint64 sum = 0;
        for(int i = 0; i < 100; ++i){
            QThread::msleep(400);
            sum += i;
            progressCallback(i/100.0);
        }
    }
    
    }

    class Emitter : public QObject
    {
        Q_OBJECT
    public:
        Q_INVOKABLE void doSomething()
        {
            auto progressCallback = [&](qreal p){
                emit progress(p);
            };
    
            auto lambda = [progressCallback](){
                Operations::longOperation(progressCallback);
            };
    
            QtConcurrent::run(lambda);
        }
    signals:
        void progress(qreal);
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        Emitter emitter;
        QObject::connect(&emitter, &Emitter::progress, [](qreal progress){
            qDebug() << "Progress" << progress;
        });
        emitter.doSomething();
    
        return a.exec();
    }
    
    
    #include "main.moc"

Now my question is using the progressCallback as defined above thread safe?现在我的问题是使用上面定义的progressCallback线程安全吗? The callback will clearly be triggered from a thread different than the GUI thread, so effectively it's calling emitter.progress() directly on the QObject.显然,回调将从与 GUI 线程不同的线程触发,因此它实际上是直接在 QObject 上调用emitter.progress()

Ok, so I now realised that the code above may not be thread safe.好的,所以我现在意识到上面的代码可能不是线程安全的。 Part of my confusion was what does the emit keyword actually does.我的部分困惑是emit关键字实际上做了什么。 It turns out it's actually not much at all.事实证明,它实际上并不多。 So calling the signal from another thread is not really the best idea.所以从另一个线程调用信号并不是最好的主意。 Instead, one of way of improve the situation is replace the progressCallback with:相反,改善这种情况的一种方法是将 progressCallback 替换为:

        auto progressCallback = [&](qreal p){
            QMetaObject::invokeMethod(this, [this, p](){ emit progress(p);}, Qt::QueuedConnection);
        };

This way the signal is emitted on the thread where the Emitter lives as the lambda slot will be executed "when control returns to the event loop of the receiver's thread" as per the Qt documentation.通过这种方式,信号在发射器所在的线程上Emitter ,因为根据 Qt 文档,“当控制返回到接收器线程的事件循环时”将执行 lambda 插槽。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM