本文介绍了当移到另一个线程时,为什么会出现套接字(TCP/Web)的运行时错误? QObject :: moveToThread()是一个同步调用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将一些套接字从主线程移到工作线程,并在新线程上处理readyRead()close()write()等.我很少看到以下危险警告:

I am moving some sockets from main thread to worker thread and processing readyRead(), close(), write() etc. on the new thread. Rarely I see below dangerous warning:

通常,此警告后执行将导致未定义的行为(崩溃/未捕获的异常/正常).
检查所有信号/插槽到插座,它们似乎正确.根据我的经验,如果出现编码错误,通常会频繁或快速地发出上述警告.

Usually the execution after this warning results in undefined behaviour (crash / uncaught exception / normal).
Have checked all the signal/slot to the socket and they seem proper. From my experience, usually the above warning will be frequent or quick if there is any coding irregularity.

现在,我怀疑moveToThread()是否按预期工作!因为从 QEvent 文档中提出了QEvent::ThreadChange作为此函数调用期间当前线程上的最后一个事件.

Now I am suspecting if moveToThread() does its job as expected or not! Because from QEvent documentation a QEvent::ThreadChange is raised as the last event on the current thread during this function call.

在我的代码中,我没有检查此事件.相反,我认为,一旦moveToThread()完成,线程亲和性就会发生变化,并且对象上的新信号/插槽"将在新线程上得到保证.

In my code, I am not checking for this event. Rather I assume that, once the moveToThread() is finished, the thread affinity is changed and the new "signal/slot" on the object is guaranteed on the new thread.

问题:

  1. 将套接字移动到另一个线程是否安全?
  2. moveToThread()是否可以确保在此函数之后,该对象上的信号也移至新线程?
  3. 应如何设计以确保Socket没有线程异常?
  1. Is it safe to move the sockets to another thread?
  2. Will moveToThread() assure that the signals on that object are also moved to the new thread just after this function?
  3. How should it be designed to assure no threading irregularity with Sockets?


BTW,在最新的Qt调试器中,它位于以下代码段:


BTW, in the latest Qt debugger it lands on following code segment:

// qsocketnotifier.cpp
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
    qWarning("QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread");
    return;
}

下面是主线程的堆栈框架:

Below is the stack frame for the main thread:

下面是工作线程的堆栈框架,该框架在日志记录中暂停:

Below is the stack frame for the worker thread, which halts on the logging:

推荐答案

moveToThread()习语不支持Qt套接字!!!

我希望Qt如上所述将这一事实记录为LOUD.我花了数周的时间调试了一个并非旨在解决的问题.套接字moveToThread()问题是如此复杂,以致于无法始终如一地发生.只有在测试了具有许多套接字打开/关闭方案的自定义QTcpServer子类之后,我才能始终如一地重现它.

Qt sockets are NOT supported with moveToThread() idiom!!!

I wish Qt had documented this fact as LOUD as above. I have spent weeks of debugging an issue which is not meant for fixing. Socket moveToThread() issue is so complicated, that it doesn't happen consistently. I could reproduce it consistently only after testing a customised QTcpServer subclass with lots of socket open/close scenario.

后来我碰到了这篇文章:如何执行QTcpSocket是否在另一个线程中?. 此答案明确指出,不支持将套接字用于线程间的移动.

Later I came across this post: How do I execute QTcpSocket in a different thread?. This answer explicitly states that sockets are not supported for the movement across the threads.

以下是 QTcpServer

注意:如果要在另一个线程中将传入连接作为新的QTcpSocket对象处理,则必须将"socketDescriptor"传递给另一个线程并在其中创建QTcpSocket对象并使用其setSocketDescriptor()方法.

Note: If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the "socketDescriptor" to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.

也许这也将适用于QWebSocketServer

所以最好的方法是重载incomingConnection()并在另一个线程中调用其内部.

So the best approach is to overload incomingConnection() and call its internal in another thread.

void MyTcpServer::incomingConnection (qintptr socketDescriptor) override
{
  emit SignalToDifferentThread(socketDescriptor);
}

如果这样做,即使在注释中已提及,也可能不需要addPendingConnection()nextPendingConnection()惯用语.

If done so, the addPendingConnection() and nextPendingConnection() idiom may not be required, even though it's mentioned in a note.


QWebSocketServerQTcpServer

严格

这是另一个事实,这浪费了我更多的时间.在QTcpServer中,我们可以重载incomingConnection()并将套接字描述符传递给另一个线程以创建套接字.但是,QWebSocketServer没有这样的重载.主要是因为QTcpServer中收到的QSslSocket被升级为QWebSocket,并通过handleConnection()方法传递给QWebSocketServer.


QWebSocketServer is stricter than QTcpServer

This is another fact, which wasted more time for me. In QTcpServer, we can overload the incomingConnection() and pass on the socket descriptor to another thread to create a socket. However, QWebSocketServer doesn't have such overload. Mostly because, QSslSocket received in QTcpServer gets upgraded to QWebSocket and passed on to the QWebSocketServer via its handleConnection() method.

因此,必须在QWebSocketServer所在的位置创建 QWebSocket.因此,无论在哪个线程中,我们都希望创建一个QWebSocket,所有这些线程都需要具有一个QWebSocketServer对象.习惯上,我们可以让一个QTcpServer在单个端口上侦听,并且可以以负载平衡的方式将其QTcpSocket s(升级到QWebSocket s)保持传递给这些不同线程的QWebSocketServer s.

So QWebSocket must be created where QWebSocketServer resides!. Hence in whichever threads, we want a QWebSocket to be created, all those threads need to have an object of QWebSocketServer. Idiomatically we can have one QTcpServer listening on a single port and can keep passing its QTcpSockets (upgraded to QWebSockets) to these various threads' QWebSocketServers in a load balancing way.

我希望,您可以通过简单的编译错误来节省依赖Qt库的开发人员的大量时间.这将否定所有在Internet上流传的错误文章,这些文章建议如何在QTcpSocket上使用moveToThread().只需介绍以下方法:

I wish that, you can save lot of time of the developers relying on the Qt library by a simple compilation error. This will negate all the wrong articles floating around internet which suggest how to use moveToThread() on a QTcpSocket. Just introduce below method:

class QTcpServer / QTcpSocket / QWebSocketServer / QWebSocket : public QObject
{
  Q_OBJECT
  ...
  // Create this object in a desired thread instead of moving
  private: void moveToThread (QThread*) = delete; // hiding `QObject::moveToThread()`
};

更新:已提出 QTBUG-82373 .

这篇关于当移到另一个线程时,为什么会出现套接字(TCP/Web)的运行时错误? QObject :: moveToThread()是一个同步调用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 14:27