我正在构建一个小型多线程Web服务器。 QTcpSockets在主线程中获取,然后由QtConcurrent移交给QThreadPool,后者最终处理数据并发出答案。
我的问题是套接字是在主线程中创建的,并在另一个线程中处理。这在尝试写入套接字时会导致错误:
socket->write(somedata);
QObject:无法为a创建子级 处于不同线程的父级。 (父母是 QNativeSocketEngine(0x608330) parent的线程是QThread(0x600630), 当前线程是QThread(0x505f60)
干净的方法是使用
将套接字对象移动到处理线程socket->moveToThread(QThread::currentThread()).
但是,这只能在创建对象的线程中调用。此外,套接字将QTcpServer对象作为父对象,因此moveToThread()无论如何都会失败(父对象无法切换线程)。
如何在线程池运行的代码中将对象移动到QThread :: currentThread()?或者,我如何写入创建它的线程之外的套接字?
答案 0 :(得分:4)
扩展QTcpServer
,重新实现incomingConnection(int socketDescriptor)
并将套接字描述符传递给池中的线程。让QRunnable
从描述符创建QTcpSocket
并启动事件循环以接收该套接字的信号。
答案 1 :(得分:0)
布拉德利·胡格斯(Bradley Hugues)在Qt实验室写了一篇关于这个主题的文章,也许这会对你有所帮助!
答案 2 :(得分:0)
我也同意捕获incomingConnection(int)
并在工作线程中创建套接字是这里的方法。
但是你的问题是一个更基本的问题,所以请允许我展示一个解决一般问题的替代方案(将任意QObject
移动到线程池中的线程):
在主线程中:将套接字从任何线程中解除:
插座> moveToThread(nullptr);
这将暂停套接字的事件处理。然后将其传递给QtConcurrent。
在线程池上执行的函数中,可以现在调用
插座> moveToThread(的QThread :: currentThread());
这将重新开始事件处理。
如果幸运的话,套接字将再次向工作线程的事件循环重新注册其套接字通知程序,并且您可以从该工作程序提供连接。
但这里有两个 重要警告 :
QThreadPool::globalInstance()
)上。正如函数的名称所暗示的那样,该线程池实例是一个全局资源,由QtConcurrent的所有用户共享,并且在最佳情况下阻塞其工作线程会降低池的容量(因此,吞吐量),并且在最坏的情况下将死锁。QTcpSocket
的同步API,因为第一点),您需要调用QThread::currentThread()->exec()
来启动它。但是,这是一个阻塞调用,对于线程池调度程序,工作人员看起来很忙,所以它不会交给更多的任务(即QRunnable
s。)。如果您确实想要实现多线程tcp服务器,那么您需要滚动自己的线程池。