我知道我无法与线程之间的QTcpSocket通信,但我无法找到我做错了什么。QSocketNotifier:无法从另一个线程禁用套接字通知器
只要我发送数据发送信号到另一个线程中的QTcpSocket,QSocketNotifier应该禁止自己让QTcpSocket读取数据。因为我得到上述错误,它不会被禁用,readyRead()信号不会被发射。
我使用QEventLoop来实现阻塞机制(同步通信 - ModBus协议),而不锁定主循环。我不能使用waitReadyRead(),因为我有(超时)的问题与过去那种功能:
https://bugreports.qt-project.org/browse/QTBUG-24451
和
https://bugreports.qt-project.org/browse/QTBUG-14975
有人可能好奇,为什么林设立这么多线程:
线程1:每个新的通信都会创建一个新的线程并使用“全局”互斥锁来锁定它,以确保没有其他功能将执行并行通信。在该线程内部 - 准备好并锁定互斥锁之后,我通过Qt :: QueuedConnection信号与QTcpSocket进行通信,该信号位于Thread2中(与设备持续通信)。
线程2:创建并移动到线程的QTcpSocket对象。一旦我连接设备,我不想关闭插座。因为Im阻止主事件循环,我需要其他事件循环来运行来自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:插座通知者不能从另一个线程被禁用”。
如果我将这段代码:
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-> execute中设置了mutex.lock(),所以只要我的程序的其他部分运行方法execModbusCommand,它就会停止,直到其他线程将释放资源以建立通信。我不明白为什么它说“QSocketNotifier:套接字通知不能从另一个线程禁用” - 我设置了THREAD_2内的QTcpSocket在线程启动后通过将新的对象放在堆上:tcpSocket_ = new QTcpSocket(this);
我将不胜感激任何帮助,谢谢!