QSocketNotifier:套接字通知不能从另一个线程禁用



我知道我不能与线程之间的QTcpSocket通信,但我找不到我做错了什么。

一旦我发送数据发射信号到QTcpSocket是在另一个线程,QSocketNotifier应该禁用自己,让QTcpSocket读取数据。因为我得到上面的错误,它不会被禁用,readyRead()信号不会发出。

我使用QEventLoop来实现阻塞机制(同步通信- ModBus协议)而不锁定主循环。我不能使用waitReadyRead(),因为我过去使用该函数有问题(超时):

https://bugreports.qt - project.org/browse/qtbug - 24451

https://bugreports.qt - project.org/browse/qtbug - 14975

有人可能会好奇我为什么要建立这么多的线程:

Thread1:每个新的通信创建一个新的线程,并将其锁定为"全局"互斥锁,以确保没有其他函数将执行并行通信。在线程内部-在准备和锁定互斥锁之后,我通过Qt::QueuedConnection信号与QTcpSocket通信,该信号位于Thread2中(与设备不断通信)。

Thread2: QTcpSocket对象创建并移动到线程。一旦我连接上设备,我不想关闭插座。因为我阻塞主事件循环,我需要其他事件循环从QTcpSocket运行信号。

和代码:

void someFunctionThatWantToSendData( void ) // Main Thread
{
    ElfKernel::KernelSingleton::Instance().getGUIcontrol()->getCtrlUnitPtr()->execModbusCommand( someParameters, someCommunicationFunction );
}
ElfKernel::FrameReceived ControlUnit::execModbusCommand( std::list<unsigned int> paramsToCommand, FunctionStringList execCommand ) // Main Thread
{
    ModbusCommandReply* mcr = new ModbusCommandReply(); // THREAD_1!!!!!! this object run in a Thread -> ModbusCommandReply : public QThread
    auto timeout = ElfKernel::CommunicationManagerSingleton::Instance().getTimeout();
    mcr->execute( paramsToCommand, execCommand );  // executes THREAD_1
    bool hasFinishedWithoutTimeout = mcr->wait(timeout);  // waiting for THREAD_1 to finish
    FrameReceived ret = mcr->getData();
    delete mcr;
    return ret;
}
// this method is executed after preparations done by: mcr->execute( paramsToCommand, execCommand );
ElfKernel::FrameReceived CommunicationManager::getData( std::string data )  // THREAD_1
{
    emit signalTcpSocketWriteData( data );
    loop.exec();    // QEventLoop declared in a header; program loops until QTcpSocket::readyRead() signal will hit the slot TcpSocketClient::readyRead()
    FrameReceived fr = tcpSocket->readDataString();
}
// executed by: emit signalTcpSocketWriteData( data );
void TcpSocketClient::writeDataSlot( std::string data ) // THREAD_2!!!!!
{
    tcpSocket_->write( data );
    tcpSocket_->flush();
}
void TcpSocketClient::readyRead( void ) // should be executed in THREAD_2 but signal readyRead() never arrives here because a get error on Output: "QSocketNotifier: socket notifiers cannot be disabled from another thread"
{
    ElfKernel::CommunicationManagerSingleton::Instance().loop.quit();   // breaks loop.exec() in CommunicationManager::getData()
}
// constructor
CommunicationManager::CommunicationManager( void )  // Main Thread
{
    tcpSocket = new TcpSocketClient();
    tcpSocketThread = new QThread();
    tcpSocket->moveToThread( tcpSocketThread );
    QObject::connect( tcpSocketThread, SIGNAL(started()), tcpSocket, SLOT(prepare()) );
    tcpSocketThread->start();   // CREATED THREAD_2!!!!!
    QObject::connect(this, SIGNAL(signalTcpSocketWriteData(std::string)), tcpSocket, SLOT(writeDataSlot(std::string)), Qt::QueuedConnection );
    QObject::connect(this, SIGNAL(signalTcpSocketReadData(std::string *)), tcpSocket, SLOT(readDataSlot(std::string *)), Qt::QueuedConnection);
    // and other stuff
}
// constructor
TcpSocketClient::TcpSocketClient(QObject *parent)   // Main Thread - before it is moved to THREAD_2
{
    parent;
    //prepare();    // it is executed after signal QThread::started() is emitted
}
// method run after thread started
void TcpSocketClient::prepare( void )   // THREAD_2
{
    tcpSocket_ = new QTcpSocket(this);
    QObject::connect(tcpSocket_, SIGNAL(disconnected()), this, SLOT(hostHaveDisconnected()));
    QObject::connect(tcpSocket_, SIGNAL(readyRead()), this, SLOT(readyRead()));
}

因此,正如我所说的插槽TcpSocketClient::readyRead()将不会被调用,我在输出上得到消息:"QSocketNotifier: socket notifier不能从另一个线程禁用"。

如果我替换这段代码:

ElfKernel::FrameReceived ControlUnit::execModbusCommand( std::list<unsigned int> paramsToCommand, FunctionStringList execCommand ) // Main Thread
{
    ModbusCommandReply* mcr = new ModbusCommandReply(); // THREAD_1!!!!!! this object run in a Thread -> ModbusCommandReply : public QThread
    auto timeout = ElfKernel::CommunicationManagerSingleton::Instance().getTimeout();
    mcr->execute( paramsToCommand, execCommand );  // executes THREAD_1
    bool hasFinishedWithoutTimeout = mcr->wait(timeout);  // waiting for THREAD_1 to finish
    FrameReceived ret = mcr->getData();
    delete mcr;
    return ret;
}

:

ElfKernel::FrameReceived ControlUnit::execModbusCommand( std::list<unsigned int> paramsToCommand, FunctionStringList execCommand ) // Main Thread
{
    FrameReceived ret = execCommand( paramsToCommand );  // Main Thread
    return ret;
}

所以程序不是被推到线程,而是在主线程中运行(唯一的区别!)然后一切都工作- readyRead插槽工作循环中断,我得到数据。我需要运行ModbusCommandReply作为一个线程,因为我在mcr->执行中设置了一个互斥锁(),所以每当我的程序的其他部分运行execModbusCommand方法时,它将被停止,直到其他线程将释放资源来建立通信。

我不明白为什么它说"QSocketNotifier: socket notifier不能从另一个线程禁用"-我设置QTcpSocket在THREAD_2线程已经开始后,把新的对象堆:tcpSocket_ =新的QTcpSocket(this);

我将感激任何帮助,谢谢!

在主线程中我建立了一个连接:

tcpSocket->connect( ... );

在主线程中产生子对象,而不是在tcpSocketThread中。

也建议这样做:

tpcSocket->setParent(0);
// before
tcpSocket->moveToThread( tcpSocketThread );

最新更新