2013-05-16 2 views
2

Im знает, что я не могу общаться с QTcpSocket между потоками, но я не могу найти то, что Im делает неправильно.QSocketNotifier: уведомления сокетов не могут быть отключены из другого потока

Как только я посылаю сигнал передачи данных в QTcpSocket, который находится в другом потоке, QSocketNotifier должен отключить себя, чтобы позволить QTcpSocket считывать данные. Поскольку я получаю выше ошибки, он не будет отключен, и сигнал readyRead() не будет выпущен.

Im использует QEventLoop для реализации механизма блокировки (синхронная связь - протокол ModBus) без блокировки основного контура. Я не могу использовать waitReadyRead(), потому что у меня были проблемы с этой функцией в прошлом (таймауты):

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

и

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

Кто-то может быть интересно, почему Im создание так много потоков :

Thread1: Каждое новое сообщение создает новый поток и блокирует его с помощью «глобального» мьютекса, чтобы гарантировать, что никакая другая функция не будет выполнять параллельную связь. Внутри этой нити - после подготовки и блокировки мьютекса я общаюсь с QTcpSocket по сигналу Qt :: QueuedConnection, который находится в Thread2 (постоянная связь с устройством).

Thread2: Объект 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 как поток, потому что я установил mutex.lock() внутри mcr-> execute, поэтому всякий раз, когда другая часть моего метода запуска программы execModbusCommand будет остановлена, пока другой поток не освободит ресурсы для установления связи.

Я не понимаю, почему он говорит: «QSocketNotifier: сокет-оповещения не могут быть отключены из другого потока» - я установил QTcpSocket внутри THREAD_2 после того, как поток начал, поместив новый объект в кучу: tcpSocket_ = new QTcpSocket (this);

Я буду благодарен за любую помощь, спасибо!

ответ

4

в главном потоке я сделал соединение:

tcpSocket->connect(...); 

который нерест дочерний объект в главном потоке не в tcpSocketThread.

Также рекомендуется сделать:

tpcSocket->setParent(0); 
// before 
tcpSocket->moveToThread(tcpSocketThread); 
Смежные вопросы