【问题标题】:calling QThread.exec() method is necessary in QThread?在 QThread 中调用 QThread.exec() 方法是必要的吗?
【发布时间】:2011-01-21 08:04:21
【问题描述】:

我没有在我的代码中调用exec(),但timerQUdpSocket 工作正常。 exec() 是否用于等待 event 继续?

更新:timer 正在工作,因为我没有在QThread 上调用moveToThread(this),这意味着QThread 实际上仍然是main thread 的一部分。至于QUdpSocket,我使用轮询functions。所以它不需要使用signals

提示:如果您需要做init 的事情,这需要在您的QThread 中添加一个event loop,您可以delay 调用moveToThread,直到您不再需要signals,即程序加载时实用。您也不需要在构造函数中调用它(例如,您可以在 run() 中调用它),只需将 this QThread 指针复制到一个变量并使用指针将 call 稍后/其他地方。

【问题讨论】:

    标签: multithreading qt qthread


    【解决方案1】:

    您的计时器和套接字可能正在使用您调用QCoreApplication::exec() 时启动的主事件循环。 虽然我确信在线程中运行事件循环是有充分理由的,但我想不出一个。

    QThread documentation 声明:

    每个 QThread 都可以有自己的事件循环。您可以通过调用 exec(); 来启动事件循环;你可以通过调用exit() 或quit() 来停止它。在线程中拥有一个事件循环可以使用一种称为排队连接的机制将来自其他线程的信号连接到该线程中的插槽。它还可以在线程中使用需要事件循环的类,例如 QTimer 和 QTcpSocket。但是请注意,不能在线程中使用任何小部件类。

    如果没有事件循环,则可以发出由 GUI 线程或包含事件循环的不同线程处理的信号。这意味着线程必须具有事件循环才能使其插槽有效。根据上面的文档,一些类,比如QTimer,需要一个运行的事件循环,你必须调用QThread::exec()。其他类,如QTCPSocket 具有在有或没有事件循环的情况下运行的能力,具体取决于所使用的函数。类的文档应该说明它们有什么要求(如果有的话)。

    【讨论】:

    • 我能想到的是在 main.cpp 中实例化 QThread 时,即在 QMainWindow 之外。
    • 主要原因是如果您在非 GUI 线程中有多个线程或多个对象。这样可以触发和执行这些对象的槽和信号,而不会减慢 GUI 事件循环。
    • 你能添加一些代码来解释exec() 的确切位置吗?它在 QThread 的派生构造函数中吗?如果我从创建 QThread 的线程调用,它将阻塞线程。
    【解决方案2】:

    为了实际使用您的线程,而不是 QApplication 执行循环,您必须在线程构造函数中调用 moveToThread(this) 并在 QThread 派生类的受保护的 run() 方法中放置一个执行循环。

    单独的线程执行循环可防止 QApplication 循环被非 ui 相关的信号和槽弄乱,从而延迟 .e.g 按钮单击的槽执行,使您的应用程序“滞后”。

    注意:通常您总是子类 QThread,请参阅Qt doc 了解更多信息

    编辑:Qt 文档错误,请阅读此线程https://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

    【讨论】:

    • “放置执行循环”是什么意思,是指调用 QThread.exec() 还是实际实例化 QEvent 循环(尽管我不明白为什么我需要这样做,因为 QThread 提供了一个,但从答案的写法来看,这让我想到了)
    • 事件循环是执行 exec() 并在 class.exec() 语句之后阻止代码的任何事情。在这种情况下,它应该是 QThread.exec()。注意:如果你的信号频率很低,你可以放弃线程的想法,因为大部分耗时的 Qt 命令都是异步的。
    • 所以可以说我有一个计时器。现在,如果我想让我的计时器工作,我需要调用 exec。但是如果我需要在我的 run 方法中循环处理,我需要在每个循环中调用一次 exec 吗?如果计时器甚至还没有接近完成并且我想在计时器超时时进行尽可能多的处理怎么办。我在某处使用超时时间为 0 的计时器在某处阅读对于管理事件循环很有用。
    • “循环处理”我不明白你的意思。并且永远不要在任何线程中多次调用 exec() ,那是丑陋的编码风格。如果你真的需要你所有的处理能力,但仍然处理信号/槽:forever{ processEvents(); //atm 我无法提醒 qt 函数是如何调用的 extrem_cpu_intense_task(args);睡眠时间(毫秒); //给一些毫秒的休息时间 };注意:这仅适用于单独的 QThread 派生类,切勿为 UI 执行此操作,并且仅当您确实需要 大量 cpu 功率和高频率的信号/插槽发射/执行。
    • QUdpSocket(实际上是QAbstractSocket)中有2个函数,分别称为waitForReadyRead()和waitForBytesWriten()。这表明可以在不使用信号的情况下完成处理。当然,如果您的 QThread 与 gui 线程不分离,则您不能使用它们,它会生成双重释放异常。在 mandlebrot 示例中,run() 包含一个永久循环。
    【解决方案3】:

    这取决于您的程序。这是一个例子:

    void MyThread::run(){
      Curl * curl = new Curl();
      connect( curl, SIGNAL(OnTransfer(QString)), this, SLOTS(OnTransfer(QString)) );
      connect( curl, SIGNAL(OnDone()), curl, SLOTS(deleteLater()) );
      curl->Download("http://google.com");
      exec(); // this is an event loop in this thread, it will wait until you command quit()
    }
    
    void MyThread::OnTransfer(QString data){
      qDebug() << data;
    }
    

    没有 exec(),永远不会调用 OnTransfer。但是,如果您在外部创建 curl 时使用 this(假设 MyThread 父线程是主线程)作为父线程:

    MyThread::MyThread(){
      curl = new Curl(this);
      connect( curl, SIGNAL(OnTransfer(QString)), this, SLOTS(OnTransfer(QString)) );
      start();
    }
    void MyThread::run(){
      curl->Download("http://google.com");
    }
    

    这将按您的预期工作。将调用 OnTransfer。

    【讨论】:

    • 非常感谢。直到看到您的帖子,我才完全理解。
    猜你喜欢
    • 1970-01-01
    • 2020-04-07
    • 2010-11-16
    • 2019-10-19
    • 1970-01-01
    • 2015-03-26
    • 2020-05-22
    • 1970-01-01
    • 2016-04-30
    相关资源
    最近更新 更多