2

我将一个对象实例传递给一个不透明的模块,该模块在我的对象上执行 SLOT 方法(回调)。调用模块在不同的线程上。我需要在我的主线程上执行这些方法。我以为我可以用信号和槽来做到这一点,但信号方法仍在调用者线程上执行。我认为我不应该使用 moveToThread() 将我的对象移动到调用者线程 - 没有一种机制可以向我的应用程序主线程发出信号吗?

MyClass::MyClass(...)
{
   OtherClass::getInstance()->setCallback(this);
   connect(this, SIGNAL(mySignalToMainThread()), this, SLOT(doThisOnMainThread()));
}

// Public slot, called by OtherClass on its own thread.
void MyClass::someCallback() 
{
   emit mySignalToMainThread();
}

void MyClass::doThisOnMainThread()
{
   // AHHH! I am still on callers thread.
}
4

2 回答 2

1

起初,我认为从 MyClass 所在的线程上下文之外的线程上下文发射信号可能是一个问题。但是,Qt 线程文档指出

...您可以安全地从 QThread::run() 实现中发出信号,因为信号发射是线程安全的。

这几乎打消了这个想法。而且,您正在使用 a Qt::AutoConnection文档指出

如果信号是从与接收对象不同的线程发出的,则信号被排队,表现为 Qt::QueuedConnection。否则,槽被直接调用,表现为 Qt::DirectConnection。连接类型在信号发出时确定。

最后一点特别重要。如果您要编写此代码:

void MyClass::someCallback() 
{
   Q_ASSERT(QThread::currentThread() != this->thread());
   emit mySignalToMainThread();
}

void MyClass::doThisOnMainThread()
{
   Q_ASSERT(QThread::currentThread() == this->thread());
}

我希望没有断言失败,但你建议你有一个。我不得不得出结论,要么 Qt 文档是错误的,要么这个问题比你提到的更多。

于 2012-09-26T18:56:29.913 回答
0

它对我有用。也许将您的实现与我在下面的实现进行比较,看看它可能会有所不同。我试图尽可能地遵循您的约定,猜测缺少的细节:

my_class.h

class MyClass : public QObject {
  Q_OBJECT

public:
  explicit MyClass(QObject *parent = 0);

signals:
  void mySignalToMainThread();

public slots:
  void someCallback();
  void doThisOnMainThread();
};

my_class.cpp

MyClass::MyClass(QObject *parent) :
  QObject(parent) {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
  OtherClass::getInstance().setCallback(this);
  connect(this, SIGNAL(mySignalToMainThread()), SLOT(doThisOnMainThread()));
}

void MyClass::someCallback() {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
  emit mySignalToMainThread();
}

void MyClass::doThisOnMainThread() {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
}

other_class.h

class OtherClass : public QObject {
  Q_OBJECT

public:
  static OtherClass& getInstance();
  void setCallback(MyClass *cb);

public slots:
  void doCallback();

private:
  explicit OtherClass(QObject *parent = 0);

  MyClass *cb_;
};

other_class.cpp

OtherClass::OtherClass(QObject *parent) : QObject(parent) {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
}

OtherClass& OtherClass::getInstance() {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
  static OtherClass singleton;
  return singleton;
}

void OtherClass::doCallback() {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
  cb_->someCallback();
}

void OtherClass::setCallback(MyClass *cb) {
  std::cout << Q_FUNC_INFO <<  QThread::currentThreadId() << std::endl;
  cb_ = cb;
}

主文件

int main (int argc, char **argv) {
  QApplication app(argc, argv);

  MyClass c;

  QThread other;
  OtherClass::getInstance().moveToThread(&other);
  other.connect(&other, SIGNAL(started()),
                &OtherClass::getInstance(), SLOT(doCallback()));
  other.start();

  QMainWindow w;
  w.show();

  return app.exec();
}

样本输出:

__thiscall MyClass::MyClass(class QObject *)00001108
class OtherClass &__cdecl OtherClass::getInstance(void)00001108
__thiscall OtherClass::OtherClass(class QObject *)00001108
void __thiscall OtherClass::setCallback(class MyClass *)00001108
class OtherClass &__cdecl OtherClass::getInstance(void)00001108
class OtherClass &__cdecl OtherClass::getInstance(void)00001108
void __thiscall OtherClass::doCallback(void)000015AC
void __thiscall MyClass::someCallback(void)000015AC
void __thiscall MyClass::doThisOnMainThread(void)00001108
于 2012-09-26T20:06:39.223 回答