Page 1 of 2 12 LastLast
Results 1 to 20 of 26

Thread: Thread Safe Queue container....

  1. #1
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Thread Safe Queue container....

    Hello,

    I started learning about multi threading with Python then C....
    solving a classical Consumer/Producer problem...

    In python, the class Queue (and its equivalent the GLib (C)) were a good solution it seems... thread-safe queue container, one thread push stuff in it, other pop stuff from it...

    I'm porting to C++/Qt, and I was surprised to see no such convenience class inside Qt....
    QList, QQueue and co are all unsafe regarding threading.

    So I wrote a class around QQueue to make it thread safe...(with mutexes)...

    But I'm thinking that maybe such class is absent because there is a more natural way to handle the Consumer/Producer problem in a "Qt way"....

    How would you go about it?

  2. #2
    Join Date
    Sep 2009
    Location
    UK
    Posts
    2,447
    Thanks
    6
    Thanked 348 Times in 333 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Thread Safe Queue container....

    Depends on the application. If there was more reads than writes then I'd use QReadLocker & QWriteLocker, as they are based for this purpose. They also ensure the state of the lock is always well defined.

  3. #3
    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: Thread Safe Queue container....

    Producer/consumer problem is usually solved using semaphores or wait conditions, depending on the type of pattern you are dealing with (mainly if the number of producer "slots" is limited - i.e. when you produce data into a buffer of limited size).
    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.


  4. #4
    Join Date
    Sep 2009
    Location
    Tashkent, Uzbekistan
    Posts
    107
    Thanks
    1
    Thanked 4 Times in 4 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    You can use QMutex on critical ops. Check assistan for QMutex details.

  5. #5
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Quote Originally Posted by wysota View Post
    Producer/consumer problem is usually solved using semaphores or wait conditions, depending on the type of pattern you are dealing with (mainly if the number of producer "slots" is limited - i.e. when you produce data into a buffer of limited size).
    In this particular case, the container cannot be limited in size...even though it will likely have little "pressure" (1:1 - 3:1 consumers : producers, with each pop/push separated by seconds)... it is critical that the information produced by the producer is not lost....

    I ended up with:
    Qt Code:
    1. #ifndef ASYNC_queueH
    2. #define ASYNC_queueH
    3.  
    4. #include <QThread>
    5. #include <QQueue>
    6.  
    7. template<class T> class QAsyncQueue
    8. {
    9. public:
    10.  
    11. QAsyncQueue(uint _max = -1)
    12. : _max(max)
    13. {
    14. }
    15.  
    16. ~QAsyncQueue()
    17. {
    18. clean();
    19. }
    20.  
    21. uint count()
    22. {
    23. _mutex.lock();
    24. int count = _queue.count();
    25. _mutex.unlock();
    26. return count;
    27. }
    28.  
    29. bool isFull()
    30. {
    31. if (-1 == _max)
    32. return false;
    33.  
    34. _mutex.lock();
    35. int count = _queue.count();
    36. _mutex.unlock();
    37. return count >= max_;
    38. }
    39.  
    40. bool isEmpty()
    41. {
    42. _mutex.lock();
    43. bool empty = _queue.isEmpty();
    44. _mutex.unlock();
    45. return empty;
    46. }
    47.  
    48. void clean()
    49. {
    50. _mutex.lock();
    51. _queue.clear();
    52. _mutex.unlock();
    53. }
    54.  
    55. void push(const T& t)
    56. {
    57. _mutex.lock();
    58. _queue.enqueue(t);
    59. _mutex.unlock();
    60. }
    61.  
    62. T pull()
    63. {
    64. _mutex.lock();
    65. T i = _queue.dequeue();
    66. _mutex.unlock();
    67. return i;
    68. }
    69.  
    70. private:
    71.  
    72. QQueue<T> _queue;
    73. QMutex _mutex;
    74. int _max;
    75. };
    76. #endif
    To copy to clipboard, switch view to plain text mode 

    probably not state-of-the-art C++ code, but it seems to work... I declare one of these queue as a global or static and threads are sharing it....

    so from your answers so far, Qt is providing classic tools for threading...locking and such must be done by hand... there is no high level class hiding all this...

    thanks for the answers so far!
    Last edited by jcox23; 4th December 2009 at 09:32.

  6. #6
    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

    Quote Originally Posted by jcox23 View Post
    In this particular case, the container cannot be limited in size...even though it will likely have little "pressure" (1:1 - 3:1 consumers : producers, with each pop/push separated by seconds)...
    Bad idea. It's better to use a buffer of limited size with semaphores. You can't predict the order of threads being woken up by the scheduler. It may happen that your producers or consumers get a bit starved and you'll have a problem.

    By the way - your queue doesn't protect you from buffer underruns (or overruns). The fact that you call "isEmpty" or "isFull" is completely meaningless as right when you return some other thread may change its state so you may be adding to a full queue or reading from an empty one. So your queue is not thread-safe in its current form.

    it is critical that the information produced by the producer is not lost....
    Nothing will be lost. The producer will wait until there is space in the buffer.

    there is no high level class hiding all this...
    Actually there is. You could have used events to push produced data into a queue.

    Attached you will find a real thread-safe queue I just implemented.
    Attached Files Attached Files
    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.


  7. The following user says thank you to wysota for this useful post:

    jcox23 (4th December 2009)

  8. #7
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Quote Originally Posted by wysota View Post
    Attached you will find a real thread-safe queue I just implemented.
    Thanks, I will study this carefully!....

    Quote Originally Posted by wysota View Post
    By the way - your queue doesn't protect you from buffer underruns (or overruns). The fact that you call "isEmpty" or "isFull" is completely meaningless as right when you return some other thread may change its state so you may be adding to a full queue or reading from an empty one. So your queue is not thread-safe in its current form.
    I want to be sure to understand this properly...
    you're saying that a construct like:
    Qt Code:
    1. if(!asyncqueue.isEmpty()) asyncqueue.pull()
    To copy to clipboard, switch view to plain text mode 
    is not safe?
    because between the actual test of emptiness and the actual pull, another thread could have pulled, emptying the queue possibly... and so my pull will fail...
    Is that correct?
    and that's why you added the semaphore on top of the mutex protection?....

  9. #8
    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: Thread Safe Queue container....

    Quote Originally Posted by jcox23 View Post
    you're saying that a construct like:
    Qt Code:
    1. if(!asyncqueue.isEmpty()) asyncqueue.pull()
    To copy to clipboard, switch view to plain text mode 
    is not safe?
    Yes, that's correct.

    because between the actual test of emptiness and the actual pull, another thread could have pulled, emptying the queue possibly... and so my pull will fail...
    Exactly.

    and that's why you added the semaphore on top of the mutex protection?....
    Yes, the semaphore makes sure you can't read from an empty queue or write to a full queue. If you try, you will be blocked until you can. This simplifies the code and improves resource usage as your threads won't be spinning around checking if they can access the queue.
    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.


  10. #9
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Quote Originally Posted by wysota View Post
    Yes, that's correct.

    Exactly.

    Yes, the semaphore makes sure you can't read from an empty queue or write to a full queue. If you try, you will be blocked until you can. This simplifies the code and improves resource usage as your threads won't be spinning around checking if they can access the queue.
    Thanks very much for the helpful discussion...

    Btw, I got segfaults when trying tryDequeue()...(because I would prefer to have a non blocking behavior)... but don't mind me, maybe I'm not using it right...

  11. #10
    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: Thread Safe Queue container....

    The whole point is to block. Non-blocking variants are in general unreliable. And if you don't block and get a buffer overflow, you make your application unstable because you're constantly spinning in the loop waiting for the buffer to be available thus eating cpu power that could be spent on consuming messages from the buffer.

    I can't think of a single situation where you wouldn't want to block (unless blocking was more expensive than a busy loop). If you produce an element and the buffer is full and you don't want to block then what will you do with it? You can only discard it, you can't store it anywhere because you risk falling off a cliff as you are producing elements faster than you can consume them. Blocking is a way to slow down production or consumption in a graceful way.
    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. #11
    Join Date
    Sep 2009
    Location
    Tashkent, Uzbekistan
    Posts
    107
    Thanks
    1
    Thanked 4 Times in 4 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Just checked the code. I do suggest to change lock to tryLock for QMutex. Works better because much more predictable with timeouts.

  13. #12
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Quote Originally Posted by wysota View Post
    I can't think of a single situation where you wouldn't want to block (unless blocking was more expensive than a busy loop). If you produce an element and the buffer is full and you don't want to block then what will you do with it?
    that's why I wanted to used a growing container...
    the original design, was a growing container, that is only checked by consumers (only 1 or 2 thread) like every second (no cpu hog then)...

    but I'm open to new designs...and will checkout how I can deal with the blocking....
    (i'm mainly concerned on how to kill a thread that is blocking)

    Quote Originally Posted by Tanuki-no Torigava View Post
    Just checked the code. I do suggest to change lock to tryLock for QMutex. Works better because much more predictable with timeouts.
    I'll see how it goes.... but right now in the actual the tryAcquire method is causing segfaults....

  14. #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: Thread Safe Queue container....

    Quote Originally Posted by jcox23 View Post
    (i'm mainly concerned on how to kill a thread that is blocking)
    Set a flag that the thread will check right after acquiring a semaphore and release the semaphore so that threads that are being blocked can continue. They should see the flag and exit.
    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.


  15. #14
    Join Date
    Dec 2009
    Posts
    8
    Thanks
    1
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    After checking several interesting threads about data sharing among threads I chose this one to ask for suggestions.

    I am used to POSIX C thread where we can create a pointer to a queue dynamically share it among any other thread, witch could push or pull pointers into or from it.

    I used many times an old C solution where the application creates a queue of struct of pointers with thread id and network data buffer pointer. Upon request the application would create a new thread that would download data from network into a buffer, created dynamically. After download this buffer pointer would be pushed into the queue along with the thread id of the creator. Later a parser thread would process the data according to the thread id, because the parse was URL dependent, and then free the buffer memory.

    I was thinking of doing the same thing with Qt using QQueue<QNetworkReply *> but I read that QObject classes created in one thread shall not have its members called from another thread (believe it has something to do with message loop). Also heard that QNetwork and QSql modules classes shall be used in the same thread they were created.

    From what you've discussed here you came out with a thread safe container but this queue cannot contain pointer to QObject descendant, can it?

    I didn't want to copy all data from QNetworkReply into another container not inherited from QObject to then queue it but I see no other solution.
    So far this is what I came out with:
    1) the thread would dynamically create a QDataStream, wich is not QObject descendanta(is it?), and push all data from QNetworkReply into it.
    2) push QDataStream pointer into queue.
    3) parser thread would pull data out of the shared queue and free the QDataStream after parsing it.

    I think it's a waste of memory and time copying data from one container to another but was the only solution candidate I pondered so far.

    I hope you guys can give me some advices before I start coding this solution. Meanwhile I will take care of the parser function of QDataStream!
    Thanks a lot in advance!
    Last edited by ferrabras; 15th December 2009 at 11:11. Reason: mispelled After (Ater)

  16. #15
    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: Thread Safe Queue container....

    You don't need threads to handle multiple network connections with Qt.
    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.


  17. #16
    Join Date
    Dec 2009
    Posts
    8
    Thanks
    1
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    From what I know about QNetworkAccessManager I could connect a void timeout() signal from a timer to a function that requests data and connect a finished(QNetworkReply *) signal from the manager to the function that would use de data. Maybe I create a timer and a manager for each Url, this is also a good idea. I'm sure I could suppress some threads doing it. Thanks.
    Yet I would need the parser/processor to be non blocking so that they don't block the application message loop. So the parser/processor have to be "threaded" and a queue of downloaded data should be available to them. Instead of passing a QNetworkReply pointer I would have to pass a copy of the data itself anyway.

  18. #17
    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: Thread Safe Queue container....

    Quote Originally Posted by ferrabras View Post
    Yet I would need the parser/processor to be non blocking so that they don't block the application message loop. So the parser/processor have to be "threaded" and a queue of downloaded data should be available to them.
    How long do you expect the parser to parse the result? Maybe it's so short it's not worth delegating into another thread. Or maybe you can use Qt Concurrent.

    Have you seen the article on [wiki]Keeping the GUI Responsive[/wiki]?
    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.


  19. The following user says thank you to wysota for this useful post:

    bob2oneil (4th April 2011)

  20. #18
    Join Date
    Nov 2010
    Posts
    122
    Thanks
    62
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    Hi wysota, thanks for all that you do to support the Qt community. I just reviewed your fine thread safe queue template. Since this discussion is a few years old, I wonder if there have been any changes to Qt in this time (such as additional thread safe collections or other constructs) that you would utilize for this solution, or if your template implementation is as timely now as when this thread was active? Would you do anything differently with the latest release of Qt?

  21. #19
    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: Thread Safe Queue container....

    There have been no changes to Qt in this regard as far as I know.
    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.


  22. The following user says thank you to wysota for this useful post:

    bob2oneil (5th April 2011)

  23. #20
    Join Date
    Sep 2011
    Posts
    1
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Thread Safe Queue container....

    wysota, Thanks for your example! Just when I think I'm understanding it, a puzzle: At the end of tryDequeue(), shouldn't it be _semFree.release() instead of _semUsed.release() to note 1 less item in the queue, as in dequeue()?

Similar Threads

  1. Is a QProcess thread safe in Qt4?
    By Jay_D in forum Qt Programming
    Replies: 4
    Last Post: 1st September 2009, 16:38
  2. What makes something not thread safe?
    By tgreaves in forum Newbie
    Replies: 9
    Last Post: 20th February 2009, 20:16
  3. KDE/QWT doubt on debian sarge
    By hildebrand in forum KDE Forum
    Replies: 13
    Last Post: 25th April 2007, 06:13
  4. Replies: 10
    Last Post: 20th March 2007, 22:19
  5. Are QHttp n QHttpRequestHeader thread safe?
    By Shambhavi in forum Qt Programming
    Replies: 4
    Last Post: 21st January 2006, 08:33

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.