【发布时间】:2011-07-12 19:57:49
【问题描述】:
在我的程序中,我继承了 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:(解决方案)
好吧,事实证明问题并不完全是我所说的。事件循环尚未开始的事实不是问题,因为信号应该在它开始之前排队。问题是,一些信号无法及时连接到被调用——因为started() 信号在run() 被调用之前发出。
解决方案是在所有连接之后和执行之前发出另一个自定义信号。这样可以确保所有信号/插槽都连接。
这是我的 问题的解决方案,但不是真正的主题答案。我已接受确实回答标题的答案。
我将所有代码留给那些好奇的人,解决方案是等待instance() 方法中的另一个信号。
代码:
你们中的许多人都说我不能丢失信号,所以这是我的全班实现。我会将其简化为最基本的必需品。
这里是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 永远不会打印。 这里有一个竞争条件,需要知道事件循环何时开始, 为了修复它。
也许我遗漏了一些东西,而问题是不同的......
如果您真的阅读了所有内容,非常感谢!呸!
【问题讨论】:
-
您确定正在丢失信号吗?如果信号是从线程内连接的,则它们被设置为排队信号。如果它们是从没有线程的情况下连接的,它们是同步信号。查看场景的其余部分以了解信号的连接方式/时间会很有帮助。
-
@lefticus 我已经用查看问题所需的代码更新了我的帖子。最后,我包含了两次程序运行的输出。一个成功(及时创建事件循环),另一个失败(在创建事件循环之前信号丢失)。
标签: c++ multithreading qt qthread