在Qt中,当从其他线程发射信号时出现分段错误问题。

3
当我试图从另一个线程发射信号时,它会导致段错误,不确定原因。 信号和槽都在同一个类中定义,并在主GUI线程下运行,但我在由boost线程类型控制的另一个函数中调用了emit。 我正在使用Qt4,Ubuntu 10.04是我的操作系统。 这个函数是从发射信号的另一个线程中调用的。
    void MyMapItem::updateMap(std::vector<int> data11)
{
my_mutex.lock();    
cout<< "i am in updatemap"<<endl;
data12.clear();
data12=data11;
cout<<"size of data"<<data12.size()<<endl;
my_mutex.unlock();
emit mera_signal();
}

    MyMapItem::MyMapItem(QGraphicsItem *parent )
{

    QObject::connect(this,SIGNAL(mera_signal()),this,SLOT(mera_slot()),Qt::BlockingQueuedConnection );



}

这是Qt类的构造函数。
void MyMapItem::mera_slot()
{
cout<< "signal is emitted"<<endl;
qDebug() << "Date:";
} 

上面是插槽定义,我只是暂时打印一条消息。

让我更详细地说明我的流程。

  1. 我有一个类MapGenerator,它现在从QThread继承,连接到ROS并订阅主题。
  2. 现在我得到了另一个类MyMapitem,它从QObjectGraphicsItem都继承,并在这个类中定义了一个插槽和一个信号。
  3. 现在我得到了第三个类Mainwindow,它从Qobject继承,并为我设置了图形场景,采用mymapitem
  4. 现在我在main中做的是创建Mapgenerator对象并启动线程。
  5. 然后我创建了一个Mainwindow对象。
  6. 因此,当Mapgenerator线程启动时,它会从ROS订阅数据并调用MyMapItem中的一个函数并将数据传输到那里。

在这里,我想发出一个信号,以便我知道新数据已到达。然后我在Mainwindow构造函数中更新已经在场景中的项目。连接在MyMapItem类构造函数中进行。

谢谢。 这里是我的主要方法,我在其中创建线程和主窗口。

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ros::init(argc,argv,"last");
    MapGenerator::MapGenerator mg(argc,argv);
    //boost::thread ros_thread(boost::bind(&MapGenerator::init2, &mg));
    mg.start(); // Qthread 
    MainWindow w(argc,argv);
    w.show();
    return a.exec();
}

在主窗口构造函数中,我创建了Mapitem对象。

MainWindow::MainWindow( int argc, char **argv, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("My app");
    mapitem = new MyMapItem();
    scene = new QGraphicsScene(0,0,4000,4000);
    ui->graphicsView->setScene(scene);
    scene->addItem(mapitem);
}
4个回答

2
当您设置类时,通常涉及将信号连接到槽,您可以选择指定连接类型

bool QObject::connect ( const QObject * sender, const char * signal,
const QObject * receiver, const char * method,
Qt::ConnectionType type = Qt::AutoConnection ) [static]
________________ <--- 将您的连接类型指定为其中之一

默认值Qt::AutoConnection依赖于QThread的东西来知道信号是来自同一线程还是不同的线程。由于您没有使用QThreads,因此不能依赖它。根据您希望在调用线程中如何运行,明确告诉连接要作为QueuedConnectionBlockingQueuedConnection进行(有关详细信息,请参见链接)。
如果由于某些原因,您觉得始终将连接设置为其中一种类型不合适,您也可以利用QMetaObject :: invokeMethod从不同的线程中调用。请注意,此函数还允许您指定连接类型:
在对象obj上调用成员(信号或槽名称)。如果成员可以被调用,则返回true。如果不存在这样的成员或参数不匹配,则返回false。调用可以是同步或异步的,具体取决于类型:
- 如果类型是Qt::DirectConnection,则成员将立即被调用。 - 如果类型是Qt::QueuedConnection,则会发送一个QEvent,并在应用程序进入主事件循环时调用该成员。 - 如果类型是Qt::BlockingQueuedConnection,则会像Qt::QueuedConnection一样调用该方法,但当前线程将阻塞,直到事件被传递。在同一线程中的对象之间使用此连接类型进行通信会导致死锁。 - 如果类型是Qt::AutoConnection,则如果obj与调用者位于同一线程中,则同步调用成员;否则,将异步调用成员。

问题仍然存在。即使使用invokemethod,我仍然遇到了seg fault问题。 - TopGun
那我建议您发布更多的代码(包括您设置连接和/或使用invokeMethod以及它连接到的slot)。 - tmpearce
在您的编辑中,您指出您首先启动另一个线程,然后构造主窗口。问题:1)主事件循环是否已经运行(QApplication::exec())?2)当您调用MyMapItem对象时,它们是何时创建的?您确定在调用时它们是有效的项目吗? - tmpearce
MapGenerator 从哪里获取指向 MyMapItem 的指针/引用?我猜测您现在正在调用未初始化指针的成员函数,这可能是因为该项尚未构建,或者在构建该项后您忘记正确设置指针/引用。 - tmpearce
这是一个未初始化的指针,但自从我在MapGenerator构造函数中初始化它以来,它实际上发出了信号。现在问题是,在连接的槽中,我正在调用update()来刷新该项。所以它没有调用update()。我无法理解其原因。 - TopGun
显示剩余5条评论

1
QT的信号是在QObject中实现的,它了解与QT的信号和插槽相关的QObjects的各种元数据。其中一个元数据是与插槽的QObject相关联的QThread,它决定了如何调用信号的默认行为(即在同一线程中,发布到不同的QThread等)。
这很可能无法与其他线程库(如boost)很好地配合使用。当连接信号/插槽时,QObject甚至不知道插槽的QObject是否与第二个线程相关联。它可能会错误地与应用程序的主QThread相关联。
将对象与线程关联是QT相对于其他线程API的一个重要特性。非常不可能QT能够自动知道您正在使用另一个库创建线程,并能够以使QT理解线程、信号和插槽的方式使它们线程安全地与QObjects相关联。
"Threads and QObjects"中阅读更多关于QT文档的内容。

你可以明确地设置一个QueuedConnection,从一个线程发射信号到另一个线程的槽函数。 - tmpearce
@tmpearce能否回答一下如何做到呢?boost线程是否需要有某种QT事件循环才能工作?它是否需要实例化某种事件循环以安全地处理QT信号? - Doug T.
1
@DougT。boost线程不需要事件循环 - 调用的目标实际上在主QThread事件循环中。调用的源位于boost线程中。我添加了一个答案,描述如何使用不同的连接类型。 - tmpearce

0

我最近遇到了同样的问题。我有很多通过subprocess从依赖GUI线程调用的shell命令,它们都运行得很好,但是其中一个就是无法正确运行并且会崩溃。我发现的区别在于我正在尝试从主GUI线程运行它,并且当我尝试发出通常从子线程发出的信号时,它会崩溃!

我的解决方案是将需要一些shell参与的对话框部分移动到单独的QThread中,有效地继承了我的应用程序中其他线程使用的相同公共类。问题解决了! QThread是关键!

查看完整答案:https://dev59.com/M2vXa4cB1Zd3GeqPKY8k#13978817


0

你需要使用阻塞队列信号。Qt中有这个功能,可以在QThreads中使用Qt :: BlockingQueuedConnection定义连接。我不清楚boost线程是否具有此功能。


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