c++ - 了解 QThread 的事件循环何时从另一个线程开始

标签 c++ multithreading qt qthread

在我的程序中,我对 QThread 进行子类化,并实现了虚拟方法 run(),如下所示:

void ManagerThread::run() {

    // do a bunch of stuff,
    // create some objects that should be handled by this thread
    // connect a few signals/slots on the objects using QueuedConnection

    this->exec(); // start event loop
}

现在,在另一个线程(我们称之为 MainThread)中,我启动 ManagerThread等待它的 started()信号,之后我继续使用应由 ManagerThread 处理的信号和槽。但是,started() 信号本质上是在调用 run() 之前发出的,因此根据线程调度,我会丢失来自 MainThread 的一些信号,因为事件循环还没有开始!

(编辑:事实证明这不是问题,只是信号没有及时连接,但出于同样的原因)

我可以在调用 exec() 之前发出一个信号,但这也会带来麻烦。

是否有任何明确/简单的方法可以知道事件循环已经开始?

谢谢!

编辑2:(解决方案)

好吧,事实证明问题并不完全是我所说的那样。事件循环尚未启动这一事实并不是问题,因为信号应该排队直到事件循环启动为止。问题是,某些信号无法及时连接以被调用 - 因为 started() 信号是在调用 run() 之前发出的。

解决方案在所有连接之后、执行之前发出另一个自定义信号。这样可以确保所有信号/槽都已连接。

这是我的问题的解决方案,但并不是线程标题的真正答案。我已经接受了确实回答标题的答案。

我为那些好奇的人留下了下面的所有代码,解决方案是等待 instance() 方法中的另一个信号。


代码:

你们中的许多人都说我不能丢失信号,所以这是我的整个类实现。我会将其简化为最基本的必需品。

这是ManagerThread的接口(interface):

// singleton class
class ManagerThread: public QThread {

    Q_OBJECT

    // trivial private constructor/destructor

    public:
    static ManagerThread* instance();

    // called from another thread
    public:
    void doSomething(QString const& text);

    // emitted by doSomething,
    // connected to JobHandler whose affinity is this thread.
    signals:
    void requestSomething(QString const& text);

    // reimplemented virtual functions of QThread
    public:
    void run();

    private:
    static QMutex s_creationMutex;
    static ManagerThread* s_instance;
    JobHandler* m_handler; // actually handles the requests
};

一些相关的实现。创建线程的单例实例:

ManagerThread* ManagerThread::instance() {
    QMutexLocker locker(&s_creationMutex);
    if (!s_instance) {
        // start socket manager thread, and wait for it to finish starting
        s_instance = new ManagerThread();

        // SignalWaiter essentially does what is outlined here:
        // http://stackoverflow.com/questions/3052192/waiting-for-a-signal
        SignalWaiter waiter(s_instance, SIGNAL(started()));
        s_instance->start(QThread::LowPriority);
        qDebug() << "Waiting for ManagerThread to start";
        waiter.wait();
        qDebug() << "Finished waiting for ManagerThread thread to start.";
    }

    return s_instance;
}

重新实现设置信号/槽并启动事件循环的运行:

void ManagerThread::run() {
   // we are now in the ManagerThread thread, so create the handler
   m_handler = new JobHandler();

   // connect signals/slots
   QObject::connect(this,
                    SIGNAL(requestSomething(QString const&)),
                    m_handler,
                    SLOT(handleSomething(QString const&)),
                    Qt::QueuedConnection);

   qDebug() << "Starting Event Loop in ManagerThread";

   // SOLUTION: Emit signal here and wait for this one instead of started()

   this->exec(); // start event loop
}

将处理委托(delegate)给正确线程的函数。这是哪里 我发出丢失的信号:

void ManagerThread::doSomething(QString const& text) {

    qDebug() << "ManagerThread attempting to do something";

    // if calling from another thread, have to emit signal
    if (QThread::currentThread() != this) {
        // I put this sleep here to demonstrate the problem
        // If it is removed there is a large chance the event loop
        // will not start up in time to handle the subsequent signal
        QThread::msleep(2000);  
        emit(requestSomething(text));
    } else {
       // just call directly if we are already in the correct thread
       m_handler->handleSomething(text);
    }
}

最后,这是来自 MainThread 的代码,如果事件循环未及时启动,该代码将会失败:

ManagerThread::instance()->doSomething("BLAM!");

假设处理程序只是打印出其文本,以下是成功运行时打印出的内容:

Waiting for ManagerThread to start
Finished waiting for ManagerThread thread to start.
Starting Event Loop in ManagerThread
ManagerThread attempting to do something
BLAM!

以下是运行不成功时会发生的情况:

Waiting for ManagerThread to start
Finished waiting for ManagerThread thread to start.
ManagerThread attempting to do something
Starting Event Loop in ManagerThread

显然,事件循环是在信号发出后开始的,并且 BLAM 永远不会打印。 这里有一个竞争条件,需要知道事件循环何时开始, 以便修复它。

也许我遗漏了一些东西,而问题是不同的......

如果您真的阅读了所有内容,非常感谢!唷!

最佳答案

如果连接设置正确,就不会丢失信号。但如果您确实想获得有关线程事件循环开始的通知,您可以尝试 QTimer::singleShot()在调用 exec() 之前的 run() 中。它将在事件循环启动时传递,并且仅传递一次。

关于c++ - 了解 QThread 的事件循环何时从另一个线程开始,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5262012/

相关文章:

java - 如何保证线程获得最大优先级?

c++ - 使用信号和槽更新指针

c++ - Sqlite:在Qt5.4.2或vs2010中与Qt-addin一起执行Spatialite Functions

c++ - 如何在 C++ 中实现 << 接口(interface)?

multithreading - 跨度和平行循环

C# Monte Carlo Incremental Risk Calculation 优化,随机数,并行执行

c++ - 并行线程,QT

c++ - 不工作数组循环

c++ - C++ 中的成员函数

c++ - 使用可变参数模板和lambda函数进行二进制搜索