在 QThread 上下文中调用方法

发布于 2024-07-30 20:14:22 字数 1666 浏览 8 评论 0原文

在我的应用程序中,有主线程和工作线程 (QThread)。
我想从主线程调用工作线程的方法并让它在线程的上下文中运行。

我尝试使用 QMetaObject::invokeMethod 并为其提供 QueuedConnection 选项,但它不起作用。
我还尝试从主线程(连接到工作线程的插槽)发出信号,但也失败了。

以下是我尝试过的大致片段:

class Worker : public QThread
{
    Q_OBJECT

public:
    Worker() { }

    void run() 
    { 
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        exec(); 
    }

public slots:
    void doWork()
    {
        qDebug() << "executing thread id - " << QThread::currentThreadId();
    }
};

使用 QMetaObject 方式:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);

    return a.exec();
}

使用信号方式:

class Dummy : public QObject
{
    Q_OBJECT

public:
    Dummy() { }

public slots:
    void askWork() { emit work(); }

signals:
    void work();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    Dummy dummy;
    QObject::connect(&dummy, SIGNAL(work()), &worker, SLOT(doWork()), Qt::QueuedConnection);

    QTimer::singleShot(1000, &dummy, SLOT(askWork()));

    return a.exec();
}

这两种方式都会导致主线程 id 打印在 QThread doWork 中。

另外,我想过实现一个简单的生产者-消费者,但如果这有效,有什么理由不这样做呢?

In my application there's the main thread and a worker thread (QThread).
From the main thread I'd like to invoke a method of my worker thread and have it run in the thread's context.

I've tried using QMetaObject::invokeMethod and give it the QueuedConnection option but it's not working.
I've also tried emitting signals from the main thread (which is connected to the worker thread's slot) but that also failed.

Here's a snippet of roughly what I tried:

class Worker : public QThread
{
    Q_OBJECT

public:
    Worker() { }

    void run() 
    { 
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        exec(); 
    }

public slots:
    void doWork()
    {
        qDebug() << "executing thread id - " << QThread::currentThreadId();
    }
};

Using the QMetaObject way:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);

    return a.exec();
}

Using the signal way:

class Dummy : public QObject
{
    Q_OBJECT

public:
    Dummy() { }

public slots:
    void askWork() { emit work(); }

signals:
    void work();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    Dummy dummy;
    QObject::connect(&dummy, SIGNAL(work()), &worker, SLOT(doWork()), Qt::QueuedConnection);

    QTimer::singleShot(1000, &dummy, SLOT(askWork()));

    return a.exec();
}

Both ways result in the main thread id being printed in the QThread doWork.

Also, I thought of implementing a simple producer-consumer but if this works, is there any reason why not to do it this way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

┈┾☆殇 2024-08-06 20:14:22

此示例展示了如何拆分 Worker 类,以使其按照您的需要工作。 您还需要提供对 Worker 实例的引用或指针才能连接到插槽。

class Worker : public QObject
{
    Q_OBJECT

public:
    Worker() { }

public slots:
    void doWork()
    {
        qDebug() << "executing thread id - " << QThread::currentThreadId();
    }
};

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    void run()
    {
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        Worker worker;
        exec();
    }
};

This example shows how you can split up the Worker class, to make it work as you want. You also need to make available a reference or pointer to the Worker instance to be able to connect to the slot.

class Worker : public QObject
{
    Q_OBJECT

public:
    Worker() { }

public slots:
    void doWork()
    {
        qDebug() << "executing thread id - " << QThread::currentThreadId();
    }
};

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    void run()
    {
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        Worker worker;
        exec();
    }
};
你怎么敢 2024-08-06 20:14:22

问题在于接收者(QThread)“存在”于主线程中,因此主线程的事件循环是执行该槽的事件循环。

来自 Qt 的文档:

对于排队连接,当控制权返回到对象所属线程的事件循环时,将调用槽。 该槽在接收者对象所在的线程中执行。

所以到目前为止我找到的解决方案是在线程的 run() 内部创建一个对象并使用它的插槽。 这样,接收者的所有者就是线程,然后在线程上下文中调用槽。

The problem was that the receiver (the QThread) 'lives' in the main thread and thus the main thread's event loop is the one that executes the slot.

from Qt's docs:

With queued connections, the slot is invoked when control returns to the event loop of the thread to which the object belongs. The slot is executed in the thread where the receiver object lives.

So the solution I found so far was to create an object inside the thread's run() and use its slots instead. That way the receiver's owner is the thread and then the slot is called in the threads context.

你的背包 2024-08-06 20:14:22

对于简单的生产者-消费者示例,请查看 Bradley T. Hughes 的博客文章 行走时不头痛

For the simple producer-consumer example, have a look at the blog entry from Bradley T. Hughes Treading without the headache.

夏天碎花小短裙 2024-08-06 20:14:22

Worker 是在主线程中创建的,因此它的事件是在主线程中处理的。 您必须将 Worker 移动到它自己的线程:

Worker worker;
worker.moveToThread(&worker);
worker.start();

现在 Qt 知道 worker 位于新线程中,并将在该事件循环中对事件进行排队。

The Worker is created in the main thread, and therefore it's events are proccessed in the main thread. You must move the Worker to it's own thread:

Worker worker;
worker.moveToThread(&worker);
worker.start();

Now Qt knows worker lives in the new thread, and will queue events in that event loop.

腹黑女流氓 2024-08-06 20:14:22

看起来您的工作线程在您调用任何函数或向其发送信号之前就已完成。

It looks like your worker thread completes before you can even invoke any function or send a signal to it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文