知道QThread的事件循环何时从另一个线程开始

时间:2011-03-10 15:39:59

标签: 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()之前发出信号,但这也是在寻找麻烦。

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

谢谢!

EDIT2:(A液)

好吧,事实证明问题并不完全是我所说的。事件循环尚未启动的事实不是问题,因为信号排队,直到它开始。问题是,由于started()信号是在调用run()之前发出的,因此某些信号无法及时连接被调用。

解决方案 在所有连接之后和exec之前发出另一个自定义信号。这样就可以确保连接所有信号/插槽。

这是我的问题的解决方案,但不是线程标题的答案。我接受了 回答标题的答案。

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


CODE:

你们许多人都说我不能丢失信号,所以这是我的全班实施。我会将它简化为必需品。

以下是ManagerThread的接口:

// 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
}

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

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!");

假设处理程序只是打印出它的文本,这就是在成功运行时打印出来的内容:

  

等待ManagerThread开始   完成等待ManagerThread线程启动。
  在ManagerThread中启动事件循环
  ManagerThread试图做某事
  BLAM!

以下是在不成功的运行中发生的事情:

  

等待ManagerThread开始   完成等待ManagerThread线程启动。
  ManagerThread试图做某事
  在ManagerThread中启动事件循环

显然,事件循环在信号发出后开始,而BLAM从不打印。 这里有一个竞争条件,需要知道事件循环何时开始, 为了解决它。

也许我错过了什么,问题就是不同......

非常感谢你真的读过这一切!呼!

4 个答案:

答案 0 :(得分:3)

如果您正确设置连接,则不应丢失信号。但是如果你真的想要了解线程事件循环的开始,你可以在run()之前尝试QTimer::singleShot(),然后再调用exec()。它将在事件循环开始时传递,并且仅传递一次。

答案 1 :(得分:1)

您可以查看QSemaphore以在线程之间发出信号。对于同一线程上的ui事件和回调,插槽和信号更好。

修改:如果信号量不适用,您可以将QMutexQWaitCondition合并。更多示例代码可以帮助您了解如何将ManagerThread与MainThread结合使用。

答案 2 :(得分:1)

这不是问题。线程之间的信号排队(更具体地说,您需要将它们设置为在connect()调用中排队,因为线程之间的直接连接不安全。)

http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads

答案 3 :(得分:0)

您可以在ManagerThread的构造函数中创建信号/插槽连接。这样,即使在调用run()之前,它们肯定是连接的。