2013-05-16 33 views
2

我知道我无法与线程之间的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);

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

回答

4

在主线程我已经做了连接:

tcpSocket->connect(...); 

其派生子对象在主线程不在tcpSocketThread。

还建议做:

tpcSocket->setParent(0); 
// before 
tcpSocket->moveToThread(tcpSocketThread); 
相关问题