Results 1 to 16 of 16

Thread: Networking Threading Issue

  1. #1
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Networking Threading Issue

    Okay, so I'm having trouble with trying to get networking and threading to play nice together. Without holding up the GUI thread I'm trying to launch a new thread, and on that new thread communicate with a client and do intensive operations.

    Originally I had the GUI make a new thread (I made a class which sublcassed QThread) but I kept getting runtime warnings upon write() because I guess the socket is tied to the server which is tied to the original GUI thread, so I was getting an error similar (but with different classes) to this: QObject: Cannot create children for a parent that is in a different thread.
    (Parent is server_thread(0x6125e60), parent's thread is QThread(0x282730), current thread is server_thread(0x6125e60)

    So I reworked it using the QT fortune server example and I'm still having issues (now I get the warning and it doesn't work at all), if somebody could point me to another example I can use for reference I would appreciate it, my creation is like this:
    In GUI I launch the server class with:
    Qt Code:
    1. server = new rtserver(this);
    2.  
    3. server->listen(QHostAddress::AnyIPv4, quint16(PORT_NUMBER));
    To copy to clipboard, switch view to plain text mode 

    rtserver subclasses QTcpServer, in there I overrode the incomingConnection() function, and I create the thread in there like this:
    Qt Code:
    1. st = new server_thread(mw, socket_descriptor, this);
    2. st->start();
    To copy to clipboard, switch view to plain text mode 

    Where MW is just a pointer to the MainWindow I need for the thread to do some calculations.

    Then in the server thread which subclasses QThread I simply set the socket descriptor and MainWindow pointer in the c'tor, in run() I have:
    Qt Code:
    1. mClientSocket = new QTcpSocket(this);
    2.  
    3. (mClientSocket->setSocketDescriptor(mSocketDescriptor)
    To copy to clipboard, switch view to plain text mode 

    If I run it like this, I get the error above upon socket creation and the socket never receives any data.

    If I don't pass "this" as a parent to QTcpSocket then simply this always returns 0 (error is gone):
    Qt Code:
    1. bytes_avail = mClientSocket->bytesAvailable();
    To copy to clipboard, switch view to plain text mode 

    Should I or shouldn't I be passing the server thread as a parent to QTcpSocket in my thread? I feel like I should but in the Qt Fortune example they do not.

    I've tested the networking code successfully with Qt (and it works with the old method, it responds to the client), but I was getting this parent runtime warning about accessing the socket from a different thread from the Qt library upon calling write() function, so I'm trying to fix that....and I can't!!!

    I feel like I'm not setting up the socket correctly anymore, the previous method relied on the GUI calling "nextPendingConnection()" but with the new method that doesn't get called anymore (incomingConnection() is overridden and it just spawns a new thread and starts it, the GUI thread returns after that). I don't think there's any additional code I need in the incomingConnection() override, the fortune example doesn't have any, just makes the thread and starts it, passing in the raw socket descriptor...but this whole 'passing the raw socket descriptor' bit doesn't seem to be working in the new method..

    Any advice?

    NOTE: Fortune server example I mention: http://qt-project.org/doc/qt-5/qtnet...r-example.html
    Last edited by Syndacate; 14th October 2014 at 12:05.

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    Do you call exec() in your run() method?

    I.e. do you start the thread's event loop?

    Cheers,
    _

  3. #3
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by anda_skoa View Post
    Do you call exec() in your run() method?

    I.e. do you start the thread's event loop?

    Cheers,
    _
    Thanks for your response.

    The only exec() I call is when I exec the app from the main file.

    The server_thread subclasses QThread, so after I construct it in the rtserver, I call:
    Qt Code:
    1. st->start()
    To copy to clipboard, switch view to plain text mode 

    I expected start() to exec the thread, no?

    The run() function (which in this case is just an event loop) of the server_thread gets called. The first thing it does is use the raw socket descriptor it got in the c'tor to construct a QTcpSocket in itself (so it's part of the new thread), then it goes into an event loop handling requests from client, doing calculations, and writing data back to them. It is most definitely calling run() because the socket gets constructed (which is when it gives the warning now) and printing stuff out, etc. It's just not getting data from the socket anymore.

    Since I overrode incomingConnection() in the server, I just pass the raw/native socket descriptor to the new thread and the new thread constructs the QTcpSocket around it...but the read/write doesn't seem to be working anymore and I'm not sure why... The old method called nextPendingConnection() in the original thread to get the QTcpSocket...then I passed that socket over to the server thread. Qt complained about this on the write() function saying that I shouldn't be writing to a QTcpSocket that is on a different thread, so I restructured it like this so that the QTcpSocket is constructed on the spawned thread.
    Last edited by Syndacate; 14th October 2014 at 19:35.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    The only exec() I call is when I exec the app from the main file.
    Then you are missing it in the thread's run()

    Quote Originally Posted by Syndacate View Post
    The server_thread subclasses QThread, so after I construct it in the rtserver, I call:
    Qt Code:
    1. st->start()
    To copy to clipboard, switch view to plain text mode 

    I expected start() to exec the thread, no?
    That starts the thread. The default implementation of run() calls exec() to start the thread's event loop.
    Since you overwrote that, you have to start it either by calling the base implementation of run() or calling exec() yourself.

    If you don't execute the thread's event loop then the thread will exit as soon as the run method's scope is done.

    Cheers,
    _

  5. #5
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by anda_skoa View Post
    Then you are missing it in the thread's run()


    That starts the thread. The default implementation of run() calls exec() to start the thread's event loop.
    Since you overwrote that, you have to start it either by calling the base implementation of run() or calling exec() yourself.

    If you don't execute the thread's event loop then the thread will exit as soon as the run method's scope is done.

    Cheers,
    _
    Hrm, I see..

    Alright, 2 questions:
    1. My event loop (that I wrote) is in the reimplemented run() function (at the beginning of run() is where I construct the QTcpSocket, and then I go into myevent loop, this is all in the run() function) - if I call exec() at the beginning of run() I'll never even get to my event loop to process the data from the client. I can't call it at the end of run() because code execution simply never gets down there until my event loop receives the command to terminate from client (from the socket).

    2. The Qt fortune server example (here) doesn't call exec(), yet they claim theirs works. Is exec() necessary in my case? I don't do any signal/event handling or anything, the event loop used is written by me.

    I don't think the QTcpSocket relies on Qt events to run since I was never calling exec() in my run() function and before it was working. Before it just had a framework warning since the QTcpSocket was constructed in a different thread when I called write(), but read/write() functioned as they should.


    Added after 16 minutes:


    So I was thinking with your last post about calling exec() that maybe my event loop is the "wrong" way to do it.

    So now I have this in my run() function:
    Qt Code:
    1. mClientSocket = new QTcpSocket();
    2.  
    3. mClientSocket->setSocketDescriptor(mSocketDescriptor);
    4.  
    5. connect(mClientSocket, &QTcpSocket::readyRead, this, &server_thread::data_ready);
    6.  
    7. exec();
    To copy to clipboard, switch view to plain text mode 

    Now my local data_ready() function is called.

    Problem is that this warning I've been trying to avoid like the plague is STILL HERE :'(.

    Now it says, this is the exact warning, which pops up when I write() to the socket in the data_ready() function:
    Qt Code:
    1. QObject: Cannot create children for a parent that is in a different thread.
    2. (Parent is QNativeSocketEngine(0x203a5e0), parent's thread is server_thread(0x47a5ee0), current thread is QThread(0x1f42730)
    To copy to clipboard, switch view to plain text mode 
    Last edited by Syndacate; 14th October 2014 at 20:48.

  6. #6
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    As a general rule, avoid using threads for networking unless you have a good reason to do so. Use threads when you need to do computations in parallel AND QtConcurrent does not suit your needs.

    Next, if you insist on using threads, you do not have to subclass QThread. You can "push" your socket to the thread instead.

    QThread is not a thread, it is a controller object for a thread; the QThread object itself lives in the main thread, not in the thread it controls. I believe this is the cause of the issues you are facing.

    Your original code
    Qt Code:
    1. mClientSocket = new QTcpSocket(this);
    To copy to clipboard, switch view to plain text mode 
    was likely the cause for the warning, because the socket was living in the controlled thread, but its parent, "this", was the QThread object which was living in the main thread. Why don't you allocate the socket on the thread's stack like this?
    Qt Code:
    1. QTcpSocket mClientSocket;
    To copy to clipboard, switch view to plain text mode 
    Perhaps the warning is still there because you use this same pattern with another object.

    The connection
    Qt Code:
    1. connect(mClientSocket, &QTcpSocket::readyRead, this, &server_thread::data_ready);
    To copy to clipboard, switch view to plain text mode 
    probably does not do what you expect: since the sender and receiver live in distinct threads, the connection is queued and you end up reading from the socket in the main thread. Perhaps you write to the socket from the main thread too.

    You could fix that by connecting the socket to a custom object living in the same thread. Nevertheless, I advise you not to use threads for networking unless there is a compelling reason to do so.

  7. #7
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    Alright, 2 questions:
    1. My event loop (that I wrote) is in the reimplemented run() function (at the beginning of run() is where I construct the QTcpSocket, and then I go into myevent loop, this is all in the run() function) - if I call exec() at the beginning of run() I'll never even get to my event loop to process the data from the client. I can't call it at the end of run() because code execution simply never gets down there until my event loop receives the command to terminate from client (from the socket).
    The usual event loop using run() calls exec() at the end. Basically you treat run() as another main(), which also ends in exec()

    Quote Originally Posted by Syndacate View Post
    2. The Qt fortune server example (here) doesn't call exec(), yet they claim theirs works. Is exec() necessary in my case? I don't do any signal/event handling or anything, the event loop used is written by me.
    This is because it uses the tcp socket in blocking I/O mode (using the waitForSignalName()) functions.
    You can do that as well.

    Quote Originally Posted by Syndacate View Post
    I don't think the QTcpSocket relies on Qt events to run since I was never calling exec() in my run() function and before it was working.
    Before your socket was owned by the main thread, which has an event loop running due to the exec() call in main().

    Quote Originally Posted by Syndacate View Post
    So now I have this in my run() function:
    Qt Code:
    1. mClientSocket = new QTcpSocket();
    2.  
    3. mClientSocket->setSocketDescriptor(mSocketDescriptor);
    4.  
    5. connect(mClientSocket, &QTcpSocket::readyRead, this, &server_thread::data_ready);
    6.  
    7. exec();
    To copy to clipboard, switch view to plain text mode 
    You need also to specify Qt:irectConnection as the fifth parameter, otherwise data_ready will be executed by the main thread ("this" is owned by the main thread).
    Or you create receiver object in run(), i.e. as I said above treating run() like main().

    Cheers,
    _

  8. #8
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by yeye_olive View Post
    As a general rule, avoid using threads for networking unless you have a good reason to do so. Use threads when you need to do computations in parallel AND QtConcurrent does not suit your needs.
    Maybe I shouldn't be using threads for this, the problem is that the thread that reads from the socket must do some intensive calculations and then respond to the socket..and this must happen as quickly as possible. So I don't want to hold up the main thread for obvious reasons, and I don't want the overhead of creating a new thread or process to deal with all this each time I have incoming data (spawning the thread in data_ready()). Once the client is connected the data will be relatively synchronous (that is to say commands will be sent by the client, they will be processed by the thread, which is intensive math, and then they will reply to the client). So I THINK threads are the best for this.

    Quote Originally Posted by yeye_olive View Post
    Next, if you insist on using threads, you do not have to subclass QThread. You can "push" your socket to the thread instead.
    So I was following the example from Qt in their Fortune server example. I've poked around on the net and the other method I've seen is simply creating the server 'thread' (which is just a QObject derived class) and then calling the moveToThread() function and pass the new object to that thread. From what I understand functionality wise there's no difference, I've tried both ways with similar results.

    Quote Originally Posted by yeye_olive View Post
    QThread is not a thread, it is a controller object for a thread; the QThread object itself lives in the main thread, not in the thread it controls. I believe this is the cause of the issues you are facing.
    So yeah, the object itself is in the main thread, but when I sublcass it and then in the main thread I call "new_thrad->start()", this run() function is executed on the new thread, correct? That's the effect I'm going for, which is why I create the QTcpSocket in there.

    Quote Originally Posted by yeye_olive View Post
    Your original code
    Qt Code:
    1. mClientSocket = new QTcpSocket(this);
    To copy to clipboard, switch view to plain text mode 
    was likely the cause for the warning, because the socket was living in the controlled thread, but its parent, "this", was the QThread object which was living in the main thread. Why don't you allocate the socket on the thread's stack like this?
    Qt Code:
    1. QTcpSocket mClientSocket;
    To copy to clipboard, switch view to plain text mode 
    Perhaps the warning is still there because you use this same pattern with another object.
    Since this happened in the run() function, I would think that it's not the main thread, but on the separate thread? That's why I explicitly did it in the run() function instead of the c'tor for the server_thread. If I do not pass "this" (server_thread) as parent, I do not get the warning there, but I'm not getting any action on the socket with this technique, either.

    Quote Originally Posted by yeye_olive View Post
    The connection
    Qt Code:
    1. connect(mClientSocket, &QTcpSocket::readyRead, this, &server_thread::data_ready);
    To copy to clipboard, switch view to plain text mode 
    probably does not do what you expect: since the sender and receiver live in distinct threads, the connection is queued and you end up reading from the socket in the main thread. Perhaps you write to the socket from the main thread too.

    You could fix that by connecting the socket to a custom object living in the same thread. Nevertheless, I advise you not to use threads for networking unless there is a compelling reason to do so.
    See my previous comment for my confusion about this. This was done in the run() function, and I call exec() after, so my take on it is that run() is executing under the new thread, then I call exec() after connecting it, which does not return until the thread is closed (just goes into the event loop for this thread), and should handle my connection. I'm having a bit of trouble understanding why they're in distinct threads, doesn't the thread which is executing run() essentially BECOME the event loop when I call exec()?

    I'm not sure how I can avoid threading for the networking, there has to be complex computations done..the only way I could do that w/o networking is to let the main event loop handle the networking, put it into some shared buffer gimmick, and then the thread can read it out and do the complex processing...and that's annoying b/c semaphores and mutexes opens up the door to a whole bunch of complex race conditions. I'd like to avoid that if at all possible.

    My goal in its most basic premise is to spin off a thread which does the following:
    - Receives connection from client (if necessary)
    - Communicates with client
    - Does intensive computations on server

    I feel like it should be easy to spawn a thread that does that, but it's turning out not to be the case .

    Quote Originally Posted by anda_skoa View Post
    The usual event loop using run() calls exec() at the end. Basically you treat run() as another main(), which also ends in exec()
    Though my event loop will happen first, and it will not exit until thread is ready to die. So if I'm not delivering events, updating UI, etc., I shouldn't need to call exec(), right?

    I'm now calling exec() now after setting up the socket in run() and letting the event get delivered to a new function, but the problem persists upon calling write(), I get the warning.

    Quote Originally Posted by anda_skoa View Post
    This is because it uses the tcp socket in blocking I/O mode (using the waitForSignalName()) functions.
    You can do that as well.
    I'm a bit confused as to what problems the blocking I/O solves, they do it synchronously in one go, without a loop. I do it async in an event loop in my run() function. Though the read/write/creation of the socket should be identical, no?

    Quote Originally Posted by anda_skoa View Post
    Before your socket was owned by the main thread, which has an event loop running due to the exec() call in main().
    Yeah, when I was calling nextPendingConnection() to create the socket it was owned by thread. That's what I tried to solve by subclassing QTcpServer and overriding the incomingConnection() signal, now I just take the native descriptor, pass it to a new thread, and let the thread do the socket setup in the run() function so that the socket can be owned by that thread, which has its own event loop to handle communication. This should be okay, right? Qt seems to recommend in the documentation doing it this way if you want to use a thread for networking...but theirs works, mine does not -_-.

    Quote Originally Posted by anda_skoa View Post
    You need also to specify Qt:irectConnection as the fifth parameter, otherwise data_ready will be executed by the main thread ("this" is owned by the main thread).
    Or you create receiver object in run(), i.e. as I said above treating run() like main().

    Cheers,
    _
    Why would it be called from the main thread, after I connect the data_ready() in the run() function (run() is on the new thread, right?) I call exec() in the new thread (from run()). So I would think both the connection, this, and the data_read() event loop (allowed by my exec() call) ALL happen in the new thread, no?


    --


    Thanks for all the long answers guys, apparently I'm missing something here!!

  9. #9
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    An event loop is local to a thread. Each thread may or may not be running an event loop, independently of the other threads. Each QObject is associated with a thread, which you can get with QObject::thread(). When a signal S of an object A is emitted whilst connected to a slot T of an object B, the following happens:
    - if the connection is direct, then the thread emitting S executes T (this is more or less a function call with some overhead);
    - if the connection is queued, then a message is queued in B->thread()'s event loop, and B->thread() will eventually execute T;
    - if the connection is automatic (the default), then it behaves as a direct connection if A->thread() == B->thread(), or as a queued connection otherwise.

    An object's thread is initially set to the thread executing its constructor. You can change it later on with QObject::moveToThread().

    Here is what happens in your program. Let us call the main thread M and the thread managed by the QThread N.

    Since you construct the QTcpSocket in your QThread's run() method, its thread() is N. You then connect the socket's readyRead() signal to the QThread's data_ready() slot using an automatic connection. Since the socket lives in N and the QThread lives in M, the connection behaves as a queued connection and M executes data_ready(). In order to read from the socket in N, all you have to do is move data_ready() to a custom QObject created in the QThread's run(). For example :

    Qt Code:
    1. class MyObject : public QObject {
    2. Q_OBJECT
    3. ...
    4. public slots:
    5. void data_ready();
    6. ...
    7. }
    8.  
    9. void server_thread::run() {
    10. QTcpSocket socket;
    11. MyObject o;
    12. connect(&socket, &QTcpSocket::readyRead, &o, &MyObject::data_ready);
    13. ...
    14. exec();
    15. ...
    16. }
    To copy to clipboard, switch view to plain text mode 
    If server_thread::run() does not do anything clever beyond creating the objects, setting up the connections and running exec(), you need not subclass QThread. Instead you can do the following:

    Qt Code:
    1. /* Members of some class, e.g. a main window */
    2. QTcpSocket socket;
    3. MyObject o;
    4. ...
    5.  
    6. void setup() {
    7. connect(&socket, &QTcpSocket::readyRead, &o, &MyObject::data_ready);
    8. socket.moveToThread(&t);
    9. o.moveToThread(&t);
    10. ...
    11. t.start();
    12. ...
    13. }
    To copy to clipboard, switch view to plain text mode 

    If I were you, I would try doing all the networking in the main thread and only do the heavy computation in another thread. You would not even need to worry about synchronization and the overhead of thread creation: QtConcurrent::run() would probably do just what you want with good performance (threads are recycled).

  10. #10
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    So I THINK threads are the best for this.
    Very likely.


    Quote Originally Posted by Syndacate View Post
    Though my event loop will happen first, and it will not exit until thread is ready to die. So if I'm not delivering events, updating UI, etc., I shouldn't need to call exec(), right?
    You can do that, use the socket in blocking I/O mode, e.g. by calling waitForReadyRead() to wait for incoming data.
    The asynchronous mode requires an event loop.

    Quote Originally Posted by Syndacate View Post
    I'm now calling exec() now after setting up the socket in run() and letting the event get delivered to a new function, but the problem persists upon calling write(), I get the warning.
    If you are writing from data_ready() or a method called from it then you are calling it from the main thread.
    You want to have all your processing in the new thread.

    As I already wrote, use a direct connection or use a receiver object that also belongs to the same thread as the socket.

    Quote Originally Posted by Syndacate View Post
    I'm a bit confused as to what problems the blocking I/O solves, they do it synchronously in one go, without a loop. I do it async in an event loop in my run() function. Though the read/write/creation of the socket should be identical, no?
    Yes, the problem is that your processing currently crosses thread boundaries due to the auto connection. The sender (socket) and the receiver (this) belong to different threads.

    Quote Originally Posted by Syndacate View Post
    Yeah, when I was calling nextPendingConnection() to create the socket it was owned by thread. That's what I tried to solve by subclassing QTcpServer and overriding the incomingConnection() signal, now I just take the native descriptor, pass it to a new thread, and let the thread do the socket setup in the run() function so that the socket can be owned by that thread, which has its own event loop to handle communication. This should be okay, right?
    Yes, that part of your code is fine.

    Quote Originally Posted by Syndacate View Post
    Why would it be called from the main thread, after I connect the data_ready() in the run() function (run() is on the new thread, right?) I call exec() in the new thread (from run()). So I would think both the connection, this, and the data_read() event loop (allowed by my exec() call) ALL happen in the new thread, no?
    You have two objects: the socket and "this".
    The socket belongs to the new thread, "this" belongs to the main thread.
    You connect() statement has four arguments, meaning the fifth is using the default value, Qt::AutoConnection.

    Since the two objects belong to different threads, the resulting behavior is a Qt::QueuedConnection. The slot on the receiver is invoked by the receiver's thread, in your case the main thread.

    As I wrote before, you can force the slot to be called by the sender's thread, using Qt:irectConnection, or have the receiver in the same thread as the sender, i.e. by creating a data processor instance in run().

    Cheers,
    _

  11. #11
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    Maybe I shouldn't be using threads for this, the problem is that the thread that reads from the socket must do some intensive calculations and then respond to the socket..and this must happen as quickly as possible. So I don't want to hold up the main thread for obvious reasons, and I don't want the overhead of creating a new thread or process to deal with all this each time I have incoming data (spawning the thread in data_ready()). Once the client is connected the data will be relatively synchronous (that is to say commands will be sent by the client, they will be processed by the thread, which is intensive math, and then they will reply to the client). So I THINK threads are the best for this.
    You could use a thread pool (QThreadPool and friends) here for calculations instead of managing your own thread class. As far as I know, the threads are not started and stopped as the jobs are scheduled and completed so there shouldn't be any extra overhead.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  12. #12
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by wysota View Post
    You could use a thread pool (QThreadPool and friends) here for calculations instead of managing your own thread class. As far as I know, the threads are not started and stopped as the jobs are scheduled and completed so there shouldn't be any extra overhead.
    Yeah, I think I could even use QtConcurrent for my purposes.

    Quote Originally Posted by yeye_olive View Post
    An event loop is local to a thread. Each thread may or may not be running an event loop, independently of the other threads. Each QObject is associated with a thread, which you can get with QObject::thread(). When a signal S of an object A is emitted whilst connected to a slot T of an object B, the following happens:
    - if the connection is direct, then the thread emitting S executes T (this is more or less a function call with some overhead);
    - if the connection is queued, then a message is queued in B->thread()'s event loop, and B->thread() will eventually execute T;
    - if the connection is automatic (the default), then it behaves as a direct connection if A->thread() == B->thread(), or as a queued connection otherwise.

    An object's thread is initially set to the thread executing its constructor. You can change it later on with QObject::moveToThread().

    Here is what happens in your program. Let us call the main thread M and the thread managed by the QThread N.

    Since you construct the QTcpSocket in your QThread's run() method, its thread() is N. You then connect the socket's readyRead() signal to the QThread's data_ready() slot using an automatic connection. Since the socket lives in N and the QThread lives in M, the connection behaves as a queued connection and M executes data_ready(). In order to read from the socket in N, all you have to do is move data_ready() to a custom QObject created in the QThread's run(). For example :

    Qt Code:
    1. class MyObject : public QObject {
    2. Q_OBJECT
    3. ...
    4. public slots:
    5. void data_ready();
    6. ...
    7. }
    8.  
    9. void server_thread::run() {
    10. QTcpSocket socket;
    11. MyObject o;
    12. connect(&socket, &QTcpSocket::readyRead, &o, &MyObject::data_ready);
    13. ...
    14. exec();
    15. ...
    16. }
    To copy to clipboard, switch view to plain text mode 
    If server_thread::run() does not do anything clever beyond creating the objects, setting up the connections and running exec(), you need not subclass QThread. Instead you can do the following:

    Qt Code:
    1. /* Members of some class, e.g. a main window */
    2. QTcpSocket socket;
    3. MyObject o;
    4. ...
    5.  
    6. void setup() {
    7. connect(&socket, &QTcpSocket::readyRead, &o, &MyObject::data_ready);
    8. socket.moveToThread(&t);
    9. o.moveToThread(&t);
    10. ...
    11. t.start();
    12. ...
    13. }
    To copy to clipboard, switch view to plain text mode 

    If I were you, I would try doing all the networking in the main thread and only do the heavy computation in another thread. You would not even need to worry about synchronization and the overhead of thread creation: QtConcurrent::run() would probably do just what you want with good performance (threads are recycled).
    Thanks for the in-depth and thorough explanation of the different connection types. Switching to a direct connection indeed seemed to resolve my issue. When I was thinking of "this" I was thinking it would be the object on the new thread, but I guess if it's set in the c'tor, that's simply not possible. Needed to take a second to think about objects vs threads in this case, as the class itself is still owned by the creator, even if the execution (run()) is on a different thread.

    As for QtConcurrent::run() - it looks like a very nice method to do this, and I think I WOULD be able to use that, and it would make things very nice and easy, but I think there's one problem: The intensive math that the thread is doing also results in a very large amount of data. So I think even if I used QtConcurrent::run() to start the intensive data, I'd have 2 problems: 1. How would I write to the TcpSocket still? I'm not sure how I would notify the main thread that there's data that needs to be written to the QTcpSocket that it owns, and even if I got around that, 2. Since it's a large amount of data which needs to be returned, it would hang the main thread for a bit. If it was a smaller amount of data I would think that QtConcurrent would probably be the best method.

    Quote Originally Posted by anda_skoa View Post
    If you are writing from data_ready() or a method called from it then you are calling it from the main thread.
    You want to have all your processing in the new thread.

    As I already wrote, use a direct connection or use a receiver object that also belongs to the same thread as the socket.
    Yeah, good call on that, using a direct connection seemed to resolve that issue.

    Quote Originally Posted by anda_skoa View Post
    Yes, the problem is that your processing currently crosses thread boundaries due to the auto connection. The sender (socket) and the receiver (this) belong to different threads.
    Yeah, I thought 'this' would refer to the object on the new thread, but I guess the "object" in terms of C/C++ still belongs to the main thread, it is just the execution of run() which is not part of that.

    Thanks for your help guys - ideally this would be ported to a QtConcurrent::run() method, but since it's required to push a lot of network info, I think it's better to keep the write on a separate thread.

    So now, my final solution is:
    - Subclassed server overrides incomingConnection(), and incomingConnection() simply spawns a new thread and passes it the raw descriptor
    - New thread creates TcpSocket on it and sets it up with the raw descriptor
    - New thread creates direct connection cross-thread to a handler of the function (technically on main thread), and then execs()
    - New thread upon receiving request, does intensive calculation followed by intensive write to socket

    EDIT:
    This is kind of a side topic, but it pertains to this example:
    One part that I don't fully get is how Qt in their Fortune Server example was able to get around this issue.

    It seems QTcpSocket requires an event thread to work properly, because I have this case (code from my run() function):
    Qt Code:
    1. mClientSocket = new QTcpSocket();
    2. mClientSocket->setSocketDescriptor(mSocketDescriptor)
    3.  
    4. //connect(mClientSocket, &QTcpSocket::readyRead, this, &server_thread::data_ready, Qt::DirectConnection);
    5.  
    6. //exec();
    7.  
    8. quint8 buffer[4];
    9. int bytes_avail = 0;
    10. for (;;) {
    11. bytes_avail = mClientSocket->bytesAvailable();
    12. ...
    13. ....
    To copy to clipboard, switch view to plain text mode 

    If I try to execute this, bytes_avail is always 0, bytes are never available on the socket. If I uncomment those 2 lines, in my data_ready() function, there are bytes available from client with that line of code. So it seems QTcpSocket is dependent on the event thread started by exec() in order to function properly.
    Qt Code:
    1. void server_thread::data_ready() {
    2. if (mw && mClientSocket->bytesAvailable() >= 4) ... // TRUE
    To copy to clipboard, switch view to plain text mode 

    Though in Qt's example with the Fortune Server (here) they never start the event thread. So how do I, as an end user know whether an event loop is necessary for a particular QObject to function properly? Apparently it is necessary for some functions such as bytesAvailable() but it's not for write(), disconnectFromHost(), and waitForDisconnected().
    Last edited by Syndacate; 15th October 2014 at 13:01.

  13. #13
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    1. How would I write to the TcpSocket still? I'm not sure how I would notify the main thread that there's data that needs to be written to the QTcpSocket that it owns, and even if I got around that,
    I don't understand what you mean. You have to write to the socket either way (threaded or not).

    2. Since it's a large amount of data which needs to be returned, it would hang the main thread for a bit.
    No, not really. You don't need to make any copies of the data, you can just return a pointer or something alike.

    So now, my final solution is:
    - Subclassed server overrides incomingConnection(), and incomingConnection() simply spawns a new thread and passes it the raw descriptor
    If you do that, you will quickly run out of system resources if a larger number of clients connects to your server. Context switching 200 threads on a <few>-core CPU has much overhead as well, especially if all the threads are performing computation intensive operations (i.e. they are not waiting on I/O). I thought you wanted to have one thread shared for all the clients, not a thread-per-client solution. That's a really bad approach, very DOS prone.

    A much better approach would be to have a pool of threads (even a manually managed one) and push an active client to a free thread, do whatever needs to be done there and push the socket back to the main thread. This way you can do the computations and socket writing in a thread if you really have to but still keep a small number of threads to serve a large number of clients.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  14. #14
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by Syndacate View Post
    Though in Qt's example with the Fortune Server (here) they never start the event thread.
    They use the blocking I/O mode by waiting explicitly for signals using the waitForXYZ functions (where XYZ is a signal name).

    You can think of these waitFor methods as something that runs an internal event loop until a certain signal is emitted.

    Quote Originally Posted by Syndacate View Post
    So how do I, as an end user know whether an event loop is necessary for a particular QObject to function properly?
    Almost all classes that provide operations that would otherwise block.

    Quote Originally Posted by Syndacate View Post
    Apparently it is necessary for some functions such as bytesAvailable() but it's not for write(), disconnectFromHost(), and waitForDisconnected().
    Well, waitForDisconnected() is a helper method to synchronously wait for the disconnected() signal to happen, so its very intent is to work without an external event loop.

    disconnectFromHost() will return immediately, its success is reported asynchronously via the disconnected() signal.

    write() stores the data in the socket's buffer, potentially writing to the operating system's buffer and thus sending it right away.
    If not all can be written to the operating system buffer, then it will need event processing to send the reset, hence waitForBytesWritten() if you need to do this synchronously.

    Cheers,
    _

  15. #15
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Networking Threading Issue

    As others have already written, the threaded fortune server example uses the socket's synchronous (blocking) functions. Here are the crucial parts of the example:
    Qt Code:
    1. tcpSocket.write(block); // Schedules data to be sent through the socket and returns promptly.
    2. // Some of it might be sent right away, but in general data accumulates in an
    3. // internal buffer to be sent automatically by QTcpSocket later on, when an
    4. // event loop runs and gives a chance to the OS to report that the socket can
    5. // send data.
    6. tcpSocket.disconnectFromHost(); // Schedules a disconnection and returns promptly. This may immediately
    7. // disconnect, but in general only "queues" the disconnection request
    8. // since all data in the write buffer must be sent first (and thus requires
    9. // an event loop to run).
    10. tcpSocket.waitForDisconnected(); // Synchronous blocking method. Internally does something similar to running
    11. // an event loop, and only returns when the socket has disconnected, which
    12. // only happens after all pending data has been sent (see above).
    To copy to clipboard, switch view to plain text mode 

    If I understand correctly, you have tons of data to send. You might run into a problem. If you run your whole computation and call write() without ever letting the socket a chance to send, the internal buffer will grow to contain all the data, and only when you finally run the event loop will the buffer start to be consumed. You could prevent this behaviour by checking the socket's bytesToWrite() from time to time during your computation and, when the value becomes too large, wait for some data to be written, for instance by calling waitForBytesWritten() in a loop until bytesToWrite() is small enough. Better yet, you can simulate waitForBytesWritten() by running a local event loop (see QEventLoop), and react to more kinds of events than just data being sent by the socket, e.g. the socket disconnected or a network error occured, the server notified the thread that it needs to shut down, etc.

  16. #16
    Join Date
    Jul 2011
    Location
    Santa Clara, CA
    Posts
    13
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Networking Threading Issue

    Quote Originally Posted by yeye_olive View Post
    As others have already written, the threaded fortune server example uses the socket's synchronous (blocking) functions. Here are the crucial parts of the example:
    Qt Code:
    1. tcpSocket.write(block); // Schedules data to be sent through the socket and returns promptly.
    2. // Some of it might be sent right away, but in general data accumulates in an
    3. // internal buffer to be sent automatically by QTcpSocket later on, when an
    4. // event loop runs and gives a chance to the OS to report that the socket can
    5. // send data.
    6. tcpSocket.disconnectFromHost(); // Schedules a disconnection and returns promptly. This may immediately
    7. // disconnect, but in general only "queues" the disconnection request
    8. // since all data in the write buffer must be sent first (and thus requires
    9. // an event loop to run).
    10. tcpSocket.waitForDisconnected(); // Synchronous blocking method. Internally does something similar to running
    11. // an event loop, and only returns when the socket has disconnected, which
    12. // only happens after all pending data has been sent (see above).
    To copy to clipboard, switch view to plain text mode 

    If I understand correctly, you have tons of data to send. You might run into a problem. If you run your whole computation and call write() without ever letting the socket a chance to send, the internal buffer will grow to contain all the data, and only when you finally run the event loop will the buffer start to be consumed. You could prevent this behaviour by checking the socket's bytesToWrite() from time to time during your computation and, when the value becomes too large, wait for some data to be written, for instance by calling waitForBytesWritten() in a loop until bytesToWrite() is small enough. Better yet, you can simulate waitForBytesWritten() by running a local event loop (see QEventLoop), and react to more kinds of events than just data being sent by the socket, e.g. the socket disconnected or a network error occured, the server notified the thread that it needs to shut down, etc.
    Thank you for explaining what each step does. I didn't realize that using the blocking IO would remove the need for an event loop, but now that I think about it it's pretty obvious, haha.

    As for the other part, that could be a nice aspect for long term, now I don't think it's too feasible b/c the intensive calculations are on the GPU, and splitting up the GPU kernel would require lots of time. I'll use the bytesToWrite() function before writing more to verify that it's not queuing up too much internally, good idea, thanks!

    Quote Originally Posted by anda_skoa View Post
    They use the blocking I/O mode by waiting explicitly for signals using the waitForXYZ functions (where XYZ is a signal name).

    You can think of these waitFor methods as something that runs an internal event loop until a certain signal is emitted.
    Gotcha, makes sense!

    Quote Originally Posted by anda_skoa View Post
    Well, waitForDisconnected() is a helper method to synchronously wait for the disconnected() signal to happen, so its very intent is to work without an external event loop.

    disconnectFromHost() will return immediately, its success is reported asynchronously via the disconnected() signal.

    write() stores the data in the socket's buffer, potentially writing to the operating system's buffer and thus sending it right away.
    If not all can be written to the operating system buffer, then it will need event processing to send the reset, hence waitForBytesWritten() if you need to do this synchronously.

    Cheers,
    _
    Ah, gotcha. I did not realize that was the intent of it, okay, great, thanks!

    Quote Originally Posted by wysota View Post
    I don't understand what you mean. You have to write to the socket either way (threaded or not).
    I mean if you do QtConcurrent::run() and say I spawn a new thread which adds 5 + 5 and comes up with 10, how do I write that 10 to the socket? The QTcpSocket still belongs to the main thread (where I call QtConcurrent::run()), if I try to call it from the concurrent function it will yell at me for crossing thread boundaries on QTcpSocket write(), no? The idea being that the main thread does all the networking and the spawned concurrent thread just does the computations.

    Quote Originally Posted by wysota View Post
    No, not really. You don't need to make any copies of the data, you can just return a pointer or something alike.
    Yeah, but when the write() is scheduled, the main thread would be the one sending it all, right? If it's constantly trying to send data queued up by write() I would think that it's going to appear to hang from the UI perspective. If the spawned thread does this it will go unnoticed by the UI.

    Quote Originally Posted by wysota View Post
    If you do that, you will quickly run out of system resources if a larger number of clients connects to your server. Context switching 200 threads on a <few>-core CPU has much overhead as well, especially if all the threads are performing computation intensive operations (i.e. they are not waiting on I/O). I thought you wanted to have one thread shared for all the clients, not a thread-per-client solution. That's a really bad approach, very DOS prone.

    A much better approach would be to have a pool of threads (even a manually managed one) and push an active client to a free thread, do whatever needs to be done there and push the socket back to the main thread. This way you can do the computations and socket writing in a thread if you really have to but still keep a small number of threads to serve a large number of clients.
    Good point. Actually this is just a POC so I won't need to worry about more than 1 (for now), but you're right, without protection like that it's very DOS prone or prone to running out of resources. I think you're right in that using a thread pool would be the the best way to manage what is going on if I wanted to deal with multiple clients at once.

Similar Threads

  1. Classic GUI and threading issue
    By stef13013 in forum Qt Programming
    Replies: 4
    Last Post: 3rd August 2012, 18:43
  2. Classic GUI and threading issue
    By stef13013 in forum Qt Programming
    Replies: 2
    Last Post: 3rd August 2012, 16:59
  3. Threading issue in DLL
    By CristonDK in forum Qt Programming
    Replies: 6
    Last Post: 15th June 2012, 19:07
  4. Threading Issue
    By noufalk in forum Qt Programming
    Replies: 4
    Last Post: 4th August 2007, 14:45
  5. Qt-3 and networking
    By a550ee in forum Qt Programming
    Replies: 3
    Last Post: 3rd October 2006, 12:15

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.