( QNativeSocketEngine)QObject:无法为位于不同线程中的父线程创建子级
( QNativeSocketEngine) QObject: Cannot create children for a parent that is in a different thread
我遇到了一个问题,在这里多次询问类似的情况,但是我无法在这些主题中找到解决方案。
我有一个主类,我想通过qt网络支持来扩展它,这是一个额外的类。让我为您分解源代码到相关部分:
主类
.h
class MainClass: public QObject{
Q_OBJECT
public:
[...]
private:
NetworkSupport * netSupport;
};
。.cpp
MainClass::MainClass()
{
[...]
netSupport
netSupport = new NetworkSupport(this->thread());
netSupport->start();
[...]
}
网络类
.h
class NetworkSupport : public QThread
{
Q_OBJECT
public:
NetworkSupport(QThread *mainThread);
~NetworkSupport();
QByteArray readData();
void sendData(QByteArray *msg);
public slots:
void acceptConnection();
void receive();
void run();
private:
QByteArray curMessage;
QThread* libThread;
QEventLoop initWlan;
protected:
QTcpServer server;
QTcpSocket* client;
};
。.cpp
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
server.listen(QHostAddress::Any, 5200);
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
void NetworkSupport::acceptConnection()
{
client = server.nextPendingConnection();
client->moveToThread(libThread);
if (client->state() == QAbstractSocket::ConnectedState)
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
void NetworkSupport::receive()
{
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if(client->state() == QAbstractSocket::ConnectedState)
{
client->moveToThread(libThread);
printf("Sending a message %sn",msg->data());
client->write(*msg);
client->waitForBytesWritten();
}
}
我知道我通常不需要指定这么多moveToThread()
,但如果所有这些都在那里或删除,它最终不会改变任何事情。
但是在执行时,我在sendData()
client->write(*msg)
收到错误消息,即:
[...]
Sending a message ?r
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QnativeSocketEngine(0x2afc660), parent's thread is QThread(0x1b59210), current thread is QThread(0x7f6a70026b60)
QSocketNotifier: Can only be used with threads started with QThread
[...]
它看起来像一个警告,因为程序之后仍在执行,但我没有收到来自客户端的任何消息(即 receive()
永远不会被执行),我想这是因为错误消息的最后一行。
可能是此错误消息只是误导,如果是这样,它的实际含义是什么,还是我做错了什么?
你在这段代码中做错了很多事情,我不确定从什么开始。
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
这将无济于事。 server
已经与 MainClass 实例位于同一线程中
server.listen(QHostAddress::Any, 5200);
服务器将开始侦听与主类相同的线程
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
这让我很嘲笑。这将简单地启动事件循环并几乎立即退出它。
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
这将简单地在新线程中运行,调用连接并在连接语句后立即退出线程 - 您的线程在connect
后将不再运行。此外,插槽acceptConnection
可能会在与 MainClass 相同的线程中调用。我想知道何时何地创建了 MainClass。
在我看来,你同时在为很多事情而苦苦挣扎。您可能读过 somwhere,您应该使用单独的线程进行网络通信,这样您就不会阻塞其他线程。首先尝试单线程方法。当您开始工作时,您应该考虑如何利用其他线程来满足您的需求。
奖励问题:这段代码是某种可能根本没有Qt事件循环的应用程序插件吗?或者它是全栈Qt应用程序的一部分?
在你的代码中有一些误解,Qt网络和多线程是如何工作的。
首先,在Qt文档中,他们在QTcpServer::nextPendingConnection()
上说:
返回的 QTcpSocket 对象不能从另一个线程使用。如果 您想使用来自另一个线程的传入连接,您需要 以覆盖传入连接()。
因此,您必须创建自己的 Server
类,该类继承自 QTcpServer,并覆盖incomingConnection()
。在该重写方法中,您必须将套接字描述符发布到其他线程,TCP 客户端将在其中实时处理该连接。在这个简单的例子中,我们只是发出一个信号。
signals:
void myNewConnectionSignal(DescriptorType);
protected:
void incomingConnection(DescriptorType descriptor)
{
emit myNewConnectionSignal(descriptor);
}
其次,NetworkSupport
线程有什么用?我想,您希望您的服务器对象在那里生活和工作?如果是这样,那么您必须以其他方式重写它。以下仅为服务器部分。请注意,QThread 已经有一个 QEventLoop,你可以在 run() 中通过 exec() 使用它。
...
protected:
MyServer *server;
...
NetworkSupport::NetworkSupport()
{
// this is called in the main thread, so "this" lives in main thread
server=0;
client=0;
// say thread to start, but it will be actually started within run()
start();
// sometimes it is recommended to wait here for the thread started
}
NetworkSupport::~NetworkSupport()
{
// this is called in the main thread, say this thread to stop running
quit();
// wait for completion
wait();
}
void NetworkSupport::run()
{
// this is called in server thread
server=new MyServer();
if (server->listen(QHostAddress::Any, 5200))
{
// connect our new signal to slot where we create and init the TCP client, note that Qt::QueuedConnection is used because we want acceptConnection to run in the main thread
connect(server, SIGNAL(myNewConnectionSignal(DescriptorType)), MyClientsPool, SLOT(acceptConnection(DescriptorType)), Qt::QueuedConnection);
// go for infinite event loop
exec();
}
else
{
// do you always ignore errors?
}
// the thread is stopped, let's destroy the server in the thread where it was born
delete server;
server=0;
}
客户端在您的主线程中生活和工作。不得直接从其他线程调用其方法。 NetworkSupport
也存在于主线程中:是的,它封装并管理其他线程,但作为 QObject 本身,它存在于我们创建它的线程中。下面的方法总是在主线程中执行,因为我们使用 Qt::QueuedConnection 将服务器的信号连接到 NetworkSupport::acceptConnection(),它向 Qt 表示我们希望在其 QObject 所在的线程中调用该插槽。
private slots:
void socketDisconnected();
...
void NetworkSupport::socketDisconnected()
{
if (client)
{
client->deleteLater();
client=0;
}
}
void NetworkSupport::acceptConnection(DescriptorType descriptor)
{
QTcpSocket* client=new QTcpSocket(this);
client->setSocketDescriptor(descriptor);
connect(client,SIGNAL(disconnected(),this,SLOT(socketDisconnected());
connect(client,SIGNAL(readyRead(),this,SLOT(receive());
}
void NetworkSupport::receive()
{
if (client)
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if (client)
{
client->write(*msg);
client->waitForBytesWritten();
}
}
更新
如果我们只想隐藏线程内的所有网络工作。请注意,在下面的示例中,几乎没有错误处理和大量消息数据副本。您可能希望针对生产环境对其进行优化。
// private container class incapsulated and working in the thread
class NetworkThreadContainer : public QObject
{
Q_OBJECT
public:
NetworkThreadContainer(QObject* parent=0):QObject(parent),client(0)
{
if (server.listen(QHostAddress::Any, 5200))
{
connect(&server, SIGNAL(newConnection()), this, acceptConnection());
}
else
{
// don't you want to throw exception here?
}
}
public slots:
void sendDataToClient(const QByteArray& barr)
{
if (client)
{
client->write(msg);
client->waitForBytesWritten();
}
}
void acceptConnection()
{
if (!client)
{
client = server.nextPendingConnection();
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
else
{
// what will you do with more than one client connections or reconnections?
}
}
void NetworkSupport::receive()
{
if (client)
{
QByteArray curMessage = client->readAll();
emit messageReceived(curMessage);
}
}
signals:
void messageReceived(const QByteArray&);
public:
QTcpClient* client;
QTcpServer server;
};
// thread class visible to outer program
class NetworkThread : public QThread
{
Q_OBJECT
public:
NetworkThread(QObject* parent=0):QObject(parent)
{
start();
}
~NetworkThread()
{
quit();
wait();
}
bool sendDataToClient(QByteArray* barr)
{
bool ok=false;
// async send data to container's thread
mutex.lock();
if (container)
{
ok=true;
QMetaObject::invokeMethod(
container,
"sendDataToClient",
Qt::QueuedConnection,
Q_ARG(QByteArray, *barr)
);
}
else
{
// container either is not ready yet or already destroyed
}
mutex.unlock();
return ok;
}
signals:
void messageReceived(const QByteArray&);
protected:
void run()
{
mutex.lock();
// I would suggest to catch exception here and assign the error result to some variable for further processing
container=new NetworkThreadContainer();
mutex.unlock();
connect(container,SIGNAL(messageReceived(QByteArray),this,SIGNAL(messageReceived(QByteArray)),Qt::QueuedConnection);
exec();
mutex.lock();
delete container;
mutex.unlock();
}
private:
QMutex mutex;
QPointer<NetworkThreadContainer> container;
};
- 两个线程一个使用流 Api,另一个线程创建文件失败并出现错误ERROR_SHARING_VIOLATION
- C++ 线程创建/删除与线程停止/恢复
- Qt - 如何从线程创建 QFuture
- 我可以使用Qt线程ID为每个线程创建唯一的缓存吗?
- 零MQ 后台线程创建
- OpenMP 线程创建
- GLFW & ImGui:从 main 以外的线程创建 ImGui 控件
- 对象:无法为位于不同线程中的父线程创建子级
- C++ 11:线程创建给我一个"Attempt to use a deleted function"错误
- C 的周期性线程创建
- MPI - 当数组初始化值必须为常量时,如何为工作线程创建部分数组
- 当主GUI线程被阻塞时,如何从工作线程创建无模式对话框
- 多个线程创建5个线程来计算质数
- 为线程创建模板
- 线程创建,CRT和DLL是如何完成的?
- 同步线程创建和销毁(静态)对象
- 竞争条件:一个线程创建静态对象,另一个线程在初始化完成之前使用它.如何处理
- 从不同线程创建QMainWindow
- QFuture 无法为位于不同线程中的父线程创建子级
- ( QNativeSocketEngine)QObject:无法为位于不同线程中的父线程创建子级