使用QThreadPool时如何做QObject :: moveToThread()?

时间:2010-06-27 02:12:48

标签: multithreading qt qt4

我正在构建一个小型多线程Web服务器。 QTcpSockets在主线程中获取,然后由QtConcurrent移交给QThreadPool,后者最终处理数据并发出答案。

我的问题是套接字是在主线程中创建的,并在另一个线程中处理。这在尝试写入套接字时会导致错误:

socket->write(somedata);
  

QObject:无法为a创建子级   处于不同线程的父级。   (父母是   QNativeSocketEngine(0x608330)   parent的线程是QThread(0x600630),   当前线程是QThread(0x505f60)

干净的方法是使用

将套接字对象移动到处理线程
socket->moveToThread(QThread::currentThread()).

但是,这只能在创建对象的线程中调用。此外,套接字将QTcpServer对象作为父对象,因此moveToThread()无论如何都会失败(父对象无法切换线程)。

如何在线程池运行的代码中将对象移动到QThread :: currentThread()?或者,我如何写入创建它的线程之外的套接字?

3 个答案:

答案 0 :(得分:4)

扩展QTcpServer,重新实现incomingConnection(int socketDescriptor)并将套接字描述符传递给池中的线程。让QRunnable从描述符创建QTcpSocket并启动事件循环以接收该套接字的信号。

答案 1 :(得分:0)

布拉德利·胡格斯(Bradley Hugues)在Qt实验室写了一篇关于这个主题的文章,也许这会对你有所帮助!

http://blog.qt.digia.com/2010/06/17/youre-doing-it-wrong/

答案 2 :(得分:0)

我也同意捕获incomingConnection(int)并在工作线程中创建套接字是这里的方法。

但是你的问题是一个更基本的问题,所以请允许我展示一个解决一般问题的替代方案(将任意QObject移动到线程池中的线程):

  1. 在主线程中:将套接字从任何线程中解除:

    插座> moveToThread(nullptr);

    这将暂停套接字的事件处理。然后将其传递给QtConcurrent。

  2. 在线程池上执行的函数中,可以现在调用

    插座> moveToThread(的QThread :: currentThread());

    这将重新开始事件处理。

  3. 如果幸运的话,套接字将再次向工作线程的事件循环重新注册其套接字通知程序,并且您可以从该工作程序提供连接。

    但这里有两个 重要警告

    • 您不应将阻塞任务放在全局线程池(QThreadPool::globalInstance())上。正如函数的名称所暗示的那样,该线程池实例是一个全局资源,由QtConcurrent的所有用户共享,并且在最佳情况下阻塞其工作线程会降低池的容量(因此,吞吐量),并且在最坏的情况下将死锁。
    • 如果您使用工作线程的事件循环(不使用QTcpSocket的同步API,因为第一点),您需要调用QThread::currentThread()->exec()来启动它。但是,这是一个阻塞调用,对于线程池调度程序,工作人员看起来很忙,所以它不会交给更多的任务(即QRunnable s。)。

    如果您确实想要实现多线程tcp服务器,那么您需要滚动自己的线程池。