关于QThread、QObject、线程亲和性和事件循环的混淆

3

我正在浏览以下链接:

  1. You are doing it wrong
  2. 正确使用QThread第一部分
  3. 正确使用QThread第二部分

我被一些陈述搞混了。在第一个链接中,它说:

QThread中的所有函数都是为了从创建线程而不是QThread启动的线程调用而编写和设计的。

同时它建议使用 moveToThread 将对象移动到新线程中,而不是子类化 QThread。我的问题是:

run 方法的默认实现调用 exec,它创建了一个“事件循环”,当使用 moveToThread 改变对象的“线程关联”时,所有的 slots 都将在新线程上执行,而不是在创建线程上执行,这与前面所提到的预期用途相矛盾。我是否遗漏了什么?

第二个问题:

在第三个链接中,它说:

事件队列属于线程而不是事件循环,并且由运行在此线程中的所有事件循环共享。

我的问题是一个线程如何会有多个事件循环?我的理解是,事件循环通过事件队列进行循环,直到调用 exit/terminate,并处理每个到达该队列的 event。如果这是真的,那么一个循环永远不会结束(除非调用 exit/terminate),另一个循环怎么开始呢?任何演示的示例代码将不胜感激。


你错过了《如何真正地使用QThreads;完整解释》:http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ - TheDarkKnight
@Merlin069,我已经学习过了,但并没有提到它,它和我的问题无关。 - Rakib
抱歉,我当时没有时间回答。 - TheDarkKnight
谁给这个问题投了反对票,请解释一下原因。 - TheDarkKnight
1
建议将除了QThread以外的对象移至线程中。这样,在新的线程中不会执行除run之外的任何QThread方法。 - Kuba hasn't forgotten Monica
就像Kuba所说的那样。语句“QThread中的所有函数都是为了从创建线程而不是QThread启动的线程调用而编写和设计的”基本上意味着不要这样做:“moveToThread(this);”。 - thuga
1个回答

5
“这与上述的预期使用目的相矛盾。我错过了什么吗?”
是的,我认为你误解了线程亲和性(对象所在的线程)的概念。
让我们用最简代码举个例子:-
QThread* pThread = new QThread; // QThread on the main thread
MyObject* myObj = new MyObject; // MyObject on the main thread
myObj->moveToThread(pThread);   // MyObject on the new thread, controlled by pThread
pThread->start();               // pThread instance is still on the main thread

假设此代码是从其线程亲和性为主线程的对象(如QMainWindow)创建的,则线程对象pThread正在主线程上运行;它的线程亲和性是主线程。相比之下,派生自QObject的MyObject实例myObj已移动到新线程pThread。因此,myObj的线程亲和性现在是新线程。"为QThread编写的函数"仍然直接从主线程调用,因为那是它正在运行的地方。将QThread视为线程控制器对象,而不是线程本身。这是通常不建议继承QThread的原因之一,除非您想更改QThread管理底层线程的方式。
“如何在单个线程中存在多个事件循环?”
我自己没有直接使用过这个,但我会尽力解释一下我的理解。也许其他人能够纠正或确认这一点。从Qt Documentation for QEventLoop中可以得知:-

在任何时候,您都可以创建一个QEventLoop对象并调用其上的exec()来启动本地事件循环。

QEventLoop exec的签名是:-

int QEventLoop :: exec(ProcessEventsFlags flags = AllEvents)

所以,如果您传入一组标志,那么只处理这些事件。现在,由于调用exec()会启动事件的处理,直到调用exit(),因此您可以创建一个本地事件循环,让您的程序等待一个或多个特定事件发生。
第二个事件循环是主事件循环中的本地事件循环,但由于每个事件循环都可以处理整个事件队列,而该队列由线程中所有事件循环共享,因此它可用于覆盖主事件循环中的事件处理。
如果你把事件循环的概念想象成像这样做一样(伪代码):-
QList<QEvent*> eventList;
while(!stop)
{
    // handle events in eventList
}

然后第二个事件循环会执行以下操作:-

bool bStop = false;
QList<QEvent*> eventList;
while(!bStop)
{
    // handle events in eventList
    ...
    ...
    // Inner event loop
    bool bStop = false;
    while(!bStop)
    {
        // handle events in eventList
    }
}

感谢您的回答。第一个问题仍然存在,如果我的理解是正确的,那么这篇文章就是自相矛盾的。但是您对第二个问题的回答真的很有帮助。+1 - Rakib
请问您能解释一下您在哪里看到了矛盾吗?QThread 的预期使用方式是从其对象创建的线程中调用其函数,而移动到该线程的对象的函数在新线程中运行和调用。 - TheDarkKnight
你最后的评论实际上解决了我的困惑。 - Rakib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接