【问题标题】:Qt/C++ how to wait a slot when signal emittedQt/C++如何在信号发出时等待一个槽
【发布时间】:2015-06-04 01:36:27
【问题描述】:

我在 Qt/C++ 中开发了一个应用程序,我使用信号/插槽机制在 2 个线程之间进行交互。第一个线程运行 UI/TreeWidget,第二个线程运行框架

我在一项操作上遇到了问题。

在 UI 方面,在开始我的操作之前,我将连接 UI 和框架之间的信号/插槽,如下面的 treewidget.cpp 中

connect(&m_Framework, &Framework::RequestIfNameExist, this, &TreeWidget::RequestIfNameExist);
connect(this, &TreeWidget::SendAnswerIfNameExist, &m_Framework, &Framework::NotifIfNameExist);

框架,启动并发送RequestIfNameExist:

emit RequestIfNameExist(tmpname, item, fileInfo.isDir());

while(WaitingResponse == false){
    usleep(200);
}

我添加了一个循环,因为我需要等待反馈。奇怪的是,在treewidget.cpp中,我从来没有进入过

void TreeWidget::RequestIfNameExist(QString name, TreeWidgetItem *parent, bool isFolder) {
#ifdef PULS_LOG
    QLOG_INFO() << "[TreeWidget] [RequestIfNameExist] ";
#endif
    emit SendAnswerIfNameExist(isNameExist(name, parent), isFolder);
}

我从不访问 TreeWidget 中的 RequestIfNameExist,但发出了信号。

我还在框架中放了一个while循环来等待TreeWidget的反馈

void Framework::NotifIfNameExist(QTreeWidgetItem *item, bool isFolder){

    if(item != NULL)
        item->isFolder = isFolder;

    WaitingResponse = true;

}

知道为什么框架发出的信号从未到达 treewidget 吗?它是从一会儿来的吗??

有没有办法不使用while比如“等待事件”+超时

谢谢

【问题讨论】:

  • 框架线程正在运行while循环,所以它无法执行NotifIfNameExist。让框架查询 UI 元素(如 Treewidget)很奇怪。通常 UI 会查询类似框架的后端来获取数据和 UI 状态。
  • 我想你想connect每个信号的信号类中的信号和槽。

标签: c++ multithreading qt5 signals-slots


【解决方案1】:

我的第一个想法是让任何一个线程阻塞直到另一个线程中的操作完成是一个糟糕的设计——它部分违背了拥有多个线程的目的,即允许多个操作并行运行。如果您不小心,也可能导致死锁(例如,如果两个线程几乎同时决定发出并等待!)

更好的设计是让启动方法执行emit RequestIfNameExit,然后立即返回,这样启动线程的事件循环就可以在操作期间像往常一样继续运行。然后,当另一个线程完成它的工作时,它通过发出自己的响应信号来响应,导致第一个线程中的适当/连接的槽方法被调用,此时结果在第一个线程中处理回。

也就是说,如果你坚持要在一个方法内阻止信号发射线程的执行,直到另一个线程完成执行相关的槽方法,你可以通过设置信号/槽连接来获得该行为类型为Qt::BlockingQueuedConnection(可以通过connect() 的可选额外参数指定连接类型)。如果你这样做了,那么你发出的调用在槽方法(在另一个线程中)完成执行之前不会返回。鉴于此,您可以通过将指向数据对象的指针作为信号/插槽方法签名中的参数之一传递,并让另一个线程根据需要填充该数据对象,从而从另一个线程获取结果。当发出返回时,您只需检查该数据对象的内容即可查看结果。

【讨论】:

  • 在某些情况下这种设计是完全合理的。例如,在只有两个线程的情况下,主/UI线程和一个工作线程,工作线程可能会遇到异常或文件复制失败等问题,设计者希望询问用户是否要尝试如果发生这种情况,请再次或跳过它。在这种情况下,工作线程在知道答案之前无法继续,必须停止,因为 QMessageBox 只能在主线程上执行。
  • @oblivioncth 让工作线程阻塞直到它从 GUI 线程获得响应会引入潜在的死锁,因为有时(例如,当用户退出应用程序时)GUI 会阻塞(在join())直到工作线程消失。当线程 A 等待线程 B 做某事,而线程 B 同时等待线程 A 做其他事情时,就会出现死锁。
  • 当然,确定性是可能的,并且需要注意一些事情。对于具有更复杂 UI 的应用程序来说,这可能是一个更可能出现这种情况的问题。我想在工作线程中实现一个“响应”槽最终会更安全,即使它需要在所述线程中对函数进行一些重组。
  • 它可能发生在任何允许用户在工作线程运行时退出的应用程序上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多