开发者

How to do QObject::moveToThread() when using QThreadPool?

I'm building a small multithreaded web server. The QTcpSockets are fetched in the main thread and then hand over by QtConcurrent to the QThreadPool, which eventually processes the data and sends out an answer.

My problem is that the socket is created in the main thread and processed in another one. This causes errors when trying to write to the socket:

socket->write(somedata);

QObject: Cannot create children for a parent that is in a different thread. (Parent is QNativeSocketEngine(0x608330), parent's thread is QThread(0x600630), current thread is QThread(0x505f60)

The clean way would be to move the socket object to the processing thread using

socket->moveToThread(QThread::currentThread()).

This, however, can only be called within the thread th开发者_如何学Goe object was created in. Furthermore, the socket has the QTcpServer object as parent, so moveToThread() will fail anyway (parented objects cannot switch threads).

How can I move the object to the QThread::currentThread() within the code that is run by the threadpool? Alternatively, how can I write to a socket outside the thread it was created?


Extend QTcpServer, reimplement incomingConnection(int socketDescriptor) and pass the socket descriptor to the threads in the pool. Have the QRunnable create the QTcpSocket from the descriptor and start an event loop to receive that socket's signals.


I would also agree that catching incomingConnection(int) and creating the socket in the worker thread is the way to go here.

But your problem was a more fundamental one, so allow me to show an alternative that solves the general problem (moving arbitrary QObjects to threads in a thread pool):

  1. In the main thread: dissociate the socket from any thread:

    socket->moveToThread(nullptr);

    This will suspend event processing of the socket. Then pass it to QtConcurrent.

  2. In the function executed on the thread pool, you can now call

    socket->moveToThread(QThread::currentThread());

    This will re-start event processing.

With any luck, the socket will have its socket notifiers re-registered with the worker thread's event loop again, and you can serve the connection from the worker.

There are two important caveats here, though:

  • You should not put blocking tasks on the global thread pool (QThreadPool::globalInstance()). As the name of the function suggests, that thread pool instance is a global resource, shared by all users of QtConcurrent, and blocking its worker threads in the best case reduces the capacity (thus, throughput) of the pool and in the worst case will dead-lock.
  • If you use the event loop of worker threads (to not use the synchronous API of QTcpSocket, because of the first point), you'd need to call QThread::currentThread()->exec() to start it. That, however, is a blocking call and to the thread pool scheduler, the worker will look busy, so it will not hand it more tasks (ie. QRunnables).

If you actually want to implement a multi-threaded tcp server, you'll therefore need to roll your own thread pool.


Bradley Hugues wrote a post on the Qt Labs talking about this subject, maybe this will help you a bit !

https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜