【问题标题】:Accessing Object members in another QThread访问另一个 QThread 中的对象成员
【发布时间】:2015-05-09 14:32:18
【问题描述】:

我在 Qt5Application 中有 2 个线程:

线程A:包含一堆QObject派生类对象

线程B:这个线程中的worker拥有所有指向A中对象的指针

线程 A 有时可能很忙,而线程 B 只是在那里委派信号和管理其他一些东西。它从不写入任何这些对象,但我需要检查一些从 A 中的对象返回布尔值的 getter 函数。

in ThreadB:
if (objInThrA->isFinished()) { ... }

isFinished() 返回一个布尔值。

如果线程 A 真的忙于一个函数并且我在线程 B 中调用这些 isFinished 函数,我的线程 B 会停止直到线程 A 完成它的工作,还是会这样?

【问题讨论】:

    标签: c++ multithreading qt qt5 qthread


    【解决方案1】:

    Qt 信号和槽可以在不同的线程之间使用。但有两条规则:

    1. connect() 的最后一个参数应该是Qt::QueuedConectionQt::BlockingQueuedConnection(或默认为Qt::AutoConnection,如果对象属于不同的线程,则与Qt::QueuedConnection 相同)。 QueuedConnection 表示发射器不等待信号处理完成,BlockingQueuedConnection 表示它

    2. QObject 派生类不适合在线程之间传递。在此之前应该安全地复制它们(参见 QMetaType 文档)。

    【讨论】:

      【解决方案2】:

      你不应该访问成员或直接调用另一个线程中的对象的函数。唯一安全的方法是通过信号/槽机制访问它们。您可以在ThreadB 中有一个信号并将其连接到ThreadA 中的一个插槽,该插槽返回值:

      connect(objInThrB, SIGNAL(getFinished()), objInThrA, SLOT(isFinished()), Qt::BlockingQueuedConnection);
      

      这样当你在线程 B 中发出信号时:

      bool ret = getFinished();
      

      当控制返回到线程 A 的事件循环时,线程 A 中的槽将被调用并返回值。线程 B 等待 slot 被调用,这是因为连接类型是BlockingQueuedConnection。所以请注意不要使用这种阻塞连接阻塞应用程序主线程。

      【讨论】:

      • 感谢您的回答。我不知道,我可以使用这样的信号。信号的返回类型为 void 是否正确?
      • 这不是我想要的行为。这将阻塞线程 B,直到线程 A 返回值,如果线程 A 忙,这将需要一段时间。
      • 信号可以有任何返回值,就像函数一样。如果你不想等待值返回,那么你应该在两个类中使用像互斥锁这样的锁定机制来保证它的安全。
      【解决方案3】:

      好的,我自己测试了一下。

      在线程 B 中:

      connect(this,SIGNAL(runWork()),objInThrA,SLOT(doWork()));
      
      emit runWork();
      
      QThread::sleep(2);
      qDebug() << objInThrA->isFinished();
      

      在线程 A 中:

      qDebug() << "start A sleep";
      QThread::sleep(10);
      qDebug() << "end A sleep";
      

      输出:

      start A sleep
      false
      end A sleep
      

      它可以工作,但是我仍然不确定我是否以这种方式使用它,它的正确完成和定义的行为。

      【讨论】:

        【解决方案4】:

        简短的回答是否定的。
        当您按照描述运行时,您直接从不在线程中的对象调用方法(该对象在线程 A 中,但您从线程 B 调用其方法之一)。直接调用方法不是 Qt 从标准 C++ 修改的东西,因此它不通过信号、插槽或事件循环进行操作,并且不了解您的线程模型。这意味着如果你在线程 B 中调用该方法,它就会在线程 B 中运行。

        如果您不小心,从另一个线程调用对象上的方法可能会很危险,因为它会引入并发问题。如果当线程 B 对其调用 getFinished() 时线程 A 正在更新 _mFinished 数据成员,它可能会获得部分写入的值。在您的具体示例中,布尔值恰好没有“部分写入”状态,您可能会没事。
        解决并发问题是通过对将在多个线程之间共享的元素进行的原子操作来完成的。您可以使用保证是原子读写操作的变量,如布尔值通常是,或者您可以使用锁定机制来保护这些变量。互斥锁和信号量是最常见的锁定机制,它们允许来自多个线程的锁定以限制在读取和写入变量时对变量的访问。这样做是为了避免在读取变量时将它们部分写入新值。
        如果您正在从事涉及多个线程的工作,我建议您阅读互斥锁和信号量(通用多线程数据结构),因为它们对于理解一般线程至关重要。此外,Qt 有很好的 Qt 封装版本,使它们易于使用并避免在维护它们的使用过程中出现一些简单的陷阱(例如 QMutexLocker)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-13
          • 2013-05-11
          • 1970-01-01
          • 2013-05-02
          • 2023-04-07
          • 2017-09-17
          相关资源
          最近更新 更多