Results 1 to 9 of 9

Thread: Multithreaded loading of imaes with QRunnable

  1. #1
    Join Date
    Jul 2015
    Posts
    22
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Multithreaded loading of imaes with QRunnable

    Dear all,

    I am working with legacy code in a class AddTileStackToPixmapcacheWorker with header (much simplified example):

    Qt Code:
    1. #ifndef AddTileStackToPixmapcacheWorkerQRunnableQRUNNABLE_H__
    2. #define AddTileStackToPixmapcacheWorkerQRunnableQRUNNABLE_H__
    3.  
    4. #include <QObject>
    5. #include <QRunnable>
    6. #include <QGraphicsPixmapItem>
    7.  
    8. #include <boost/gil/extension/io/jpeg_io.hpp>
    9. #include <boost/gil/extension/io/png_io.hpp>
    10. #include <boost/gil/image.hpp>
    11.  
    12.  
    13. #include <set>
    14.  
    15.  
    16. class AddTileStackToPixmapcacheWorker : public QObject, public QRunnable {
    17.  
    18. Q_OBJECT
    19. public:
    20. AddTileStackToPixmapcacheWorker (QString Filename);
    21. ~AddTileStackToPixmapcacheWorker ();
    22.  
    23. void run();
    24. void LoadImagesFromHardDriveIntoVector();
    25. bool LoadImageDataRgb8(QString fileNameRelative, QPixmap & ima);
    26.  
    27. private:
    28. QString m_Filename;
    29. };
    30.  
    31. #endif
    To copy to clipboard, switch view to plain text mode 


    and cpp:
    Qt Code:
    1. #include "AddTileStackToPixmapcacheWorker.h"
    2.  
    3. #include <iostream>
    4. #include <QDebug>
    5. #include <QThread>
    6.  
    7. AddTileStackToPixmapcacheWorker::AddTileStackToPixmapcacheWorker (QString Filenames) :
    8. m_Filename (Filenames)
    9. {
    10. }
    11.  
    12. AddTileStackToPixmapcacheWorker::~AddTileStackToPixmapcacheWorker()
    13. {
    14. }
    15.  
    16. void AddTileStackToPixmapcacheWorker::run()
    17. {
    18. QPixmap ima;
    19. bool b = LoadImageDataRgb8(m_Filename, ima);
    20. }
    21.  
    22.  
    23.  
    24. bool AddTileStackToPixmapcacheWorker::LoadImageDataRgb8( QString fileNameRelative, QPixmap & ima )
    25. {
    26. if (fileNameRelative.size())
    27. {
    28. QString fileNameAbsolute = "D:/Sharing/MicroscopyImageData/falc_Nr2_13013792_2015.04.02-12.32.10/images/" + fileNameRelative;
    29. const char * pChar;
    30.  
    31.  
    32. //check ending for .jpg or .jpeg
    33. if (fileNameAbsolute.endsWith(".jpg", Qt::CaseInsensitive) || fileNameAbsolute.endsWith(".jpeg", Qt::CaseInsensitive))
    34. {
    35. pChar = "JPG";
    36. }
    37.  
    38. ima.load(fileNameAbsolute, pChar);
    39. return true;
    40. }
    41.  
    42. return false;
    43. }
    To copy to clipboard, switch view to plain text mode 


    I use it like that:
    Qt Code:
    1. #include "AddTileStackToPixmapcacheWorker.h"
    2.  
    3. void myThreadingExample()
    4. {
    5.  
    6. std::vector<QString> vecStrings;
    7. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_0_Index_0_ds(1).jpg"));
    8. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_0_Index_0_orig.jpg"));
    9. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_1_Index_0_ds(1).jpg"));
    10. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_1_Index_0_orig.jpg"));
    11. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_10_Index_0_ds(1).jpg"));
    12. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_10_Index_0_orig.jpg"));
    13. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_11_Index_0_ds(1).jpg"));
    14. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_11_Index_0_orig.jpg"));
    15. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_12_Index_0_ds(1).jpg"));
    16. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_12_Index_0_orig.jpg"));
    17. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_13_Index_0_ds(1).jpg"));
    18. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_13_Index_0_orig.jpg"));
    19. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_14_Index_0_ds(1).jpg"));
    20. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_14_Index_0_orig.jpg"));
    21. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_15_Index_0_ds(1).jpg"));
    22. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_15_Index_0_orig.jpg"));
    23. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_16_Index_0_ds(1).jpg"));
    24. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_16_Index_0_orig.jpg"));
    25. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_17_Index_0_ds(1).jpg"));
    26. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_17_Index_0_orig.jpg"));
    27. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_18_Index_0_ds(1).jpg"));
    28. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_18_Index_0_orig.jpg"));
    29. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_19_Index_0_ds(1).jpg"));
    30. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_19_Index_0_orig.jpg"));
    31. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_2_Index_0_ds(1).jpg"));
    32. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_2_Index_0_orig.jpg"));
    33. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_20_Index_0_ds(1).jpg"));
    34. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_20_Index_0_orig.jpg"));
    35. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_21_Index_0_ds(1).jpg"));
    36. vecStrings.push_back(QString("img_Rev_1_Mag_2.5_Stack_21_Index_0_orig.jpg"));
    37.  
    38.  
    39. for (int i=0; i< vecStrings.size(); i++)
    40. {
    41. AddTileStackToPixmapcacheWorker *pAddTileStackToPixmapcacheWorker = new AddTileStackToPixmapcacheWorker(vecStrings[i]);
    42. pAddTileStackToPixmapcacheWorker->setAutoDelete(true);
    43. QThreadPool::globalInstance()->setExpiryTimeout(-1);
    44. QThreadPool::globalInstance()->start(pAddTileStackToPixmapcacheWorker);
    45. }
    46. }
    47.  
    48.  
    49.  
    50. int main(int argc, char *argv[])
    51. {
    52. QApplication app(argc, argv);
    53. myThreadingExample();
    54. return app.exec();
    55. }
    To copy to clipboard, switch view to plain text mode 

    When I run this i get stuff like the following in the console:
    Qt Code:
    1. QObject::startTimer: Timers cannot be started from another thread
    2. QObject::startTimer: Timers cannot be started from another thread
    3. QObject::startTimer: Timers cannot be started from another thread
    4. QObject::startTimer: Timers cannot be started from another thread
    5. QObject::startTimer: Timers cannot be started from another thread
    6. QObject::startTimer: Timers cannot be started from another thread
    7. QObject::startTimer: Timers cannot be started from another thread
    8. QObject::startTimer: Timers cannot be started from another thread
    To copy to clipboard, switch view to plain text mode 

    and when I close my console window I get:
    Qt Code:
    1. QObject::~QObject: Timers cannot be stopped from another thread
    2. QWaitCondition: Destroyed while threads are still waiting
    3. QWaitCondition: Destroyed while threads are still waiting
    4. QWaitCondition: Destroyed while threads are still waiting
    5. QWaitCondition: Destroyed while threads are still waiting
    6. QWaitCondition: Destroyed while threads are still waiting
    7. QWaitCondition: Destroyed while threads are still waiting
    8. QWaitCondition: Destroyed while threads are still waiting
    9. QWaitCondition: Destroyed while threads are still waiting
    To copy to clipboard, switch view to plain text mode 

    Once or twice (out of around 20 or 30 tries) it also crashed with some unhandled exception, but I don't remember what the exact error description was.

    What am I doing wrong?
    How else would I be able to load several images asynchronously?!?
    I've tried to use only one "extra" thread for loading images, which is more stable, but for my use case is also much slower than the example in this post. So, to summarize: I am looking for either:
    1) a way to fix my QRunnable example or
    2) a different multithreaded approach that works
    Any ideas?!? Many many thanks in advance!!!!!

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

    Default Re: Multithreaded loading of imaes with QRunnable

    Load into a QImage, QPixmap is a GUI resource and can only be used from the main thread.

    Why is your runnable also a QObject?
    What do you do with the image once it is loaded?
    Do you know all filenames beforehand or do you get new filenames during runtime?

    Cheers,
    _

  3. #3
    Join Date
    Jul 2015
    Posts
    22
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Multithreaded loading of imaes with QRunnable

    Thanks for the quick answer!!!

    Why is your runnable also a QObject?
    This is because in the "real project" I also need signal/slot mechanism for the class.

    What do you do with the image once it is loaded?
    I convert it to QGraphicsPixmapItem, so I can add it to a QGraphicsScene with QGraphicsScene::addItem.

    Do you know all filenames beforehand or do you get new filenames during runtime?
    I get them at run time

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

    Default Re: Multithreaded loading of imaes with QRunnable

    Quote Originally Posted by donelron View Post
    This is because in the "real project" I also need signal/slot mechanism for the class.
    Hmm.
    QObjects and thread pool are a bit problematic due to thread affinity of objects. The "owner thread" of the runnable will be the thread that created it, but it will be handled and deleted by the threadpool thread.

    If all you do is emit a signal you should be fine though.

    Quote Originally Posted by donelron View Post
    I convert it to QGraphicsPixmapItem, so I can add it to a QGraphicsScene with QGraphicsScene::addItem.
    So you emit a signal with the loaded image and then in the receiver slot create a pixmap item to display it?

    Quote Originally Posted by donelron View Post
    I get them at run time
    Sorry, I wasn't clear.
    Do you get the full list in one go or do you get files added by some form of events?

    Cheers,
    _

  5. #5
    Join Date
    Jul 2015
    Posts
    22
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Multithreaded loading of images with QRunnable

    So you emit a signal with the loaded image and then in the receiver slot create a pixmap item to display it?
    Yes, that's how it works (currently). However, I am not sure if the signal/slot approach unnecessarily slows down everything?!? Maybe the whole processing pipeline from loading images to displaying them in the scene could be faster with a "regular" function call instead of emitting a signal?!?...

    Do you get the full list in one go or do you get files added by some form of events?
    I get it by "some form of event": Whenever the application needs to display a new image a function is called that instantiates an AddTileStackToPixmapcacheWorker object. So, in contrast to my MWE the "real code" rather looks like:
    Qt Code:
    1. void AddImageInViewer(ImagePointerClass * pIamAPointerThatUniquelyIdentifiesAnImage)
    2. {
    3. AddTileStackToPixmapcacheWorker *pAddTileStackToPixmapcacheWorker = new AddTileStackToPixmapcacheWorker(pIamAPointerThatUniquelyIdentifiesAnImage);
    4. pAddTileStackToPixmapcacheWorker->setAutoDelete(true);
    5. QThreadPool::globalInstance()->setExpiryTimeout(-1);
    6. QThreadPool::globalInstance()->start(pAddTileStackToPixmapcacheWorker);}
    To copy to clipboard, switch view to plain text mode 

  6. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,418
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Multithreaded loading of images with QRunnable

    Quote Originally Posted by donelron View Post
    Yes, that's how it works (currently). However, I am not sure if the signal/slot approach unnecessarily slows down everything?!?
    You have to somehow transfer the data between threads.
    Putting the images into queue with propery locking might or might not be faster.

    The main advantage would be potentially getting more than one image per queue read.

    Maybe the whole processing pipeline from loading images to displaying them in the scene could be faster with a "regular" function call instead of emitting a signal?!?...


    Quote Originally Posted by donelron View Post
    I get it by "some form of event": Whenever the application needs to display a new image a function is called that instantiates an AddTileStackToPixmapcacheWorker object.
    Ah, ok.
    For the case of a one-time full list I would have suggested a dedicated loader thread.

    Quote Originally Posted by donelron View Post
    Qt Code:
    1. void AddImageInViewer(ImagePointerClass * pIamAPointerThatUniquelyIdentifiesAnImage)
    2. {
    3. AddTileStackToPixmapcacheWorker *pAddTileStackToPixmapcacheWorker = new AddTileStackToPixmapcacheWorker(pIamAPointerThatUniquelyIdentifiesAnImage);
    4. pAddTileStackToPixmapcacheWorker->setAutoDelete(true);
    5. QThreadPool::globalInstance()->setExpiryTimeout(-1);
    6. QThreadPool::globalInstance()->start(pAddTileStackToPixmapcacheWorker);}
    To copy to clipboard, switch view to plain text mode 
    Auto delete is true by default.
    I would also recommend to use a dedicated threadpool unless you are sure you are not needing the global instance anywhere else.

    Cheers,
    _

  7. #7
    Join Date
    Jul 2015
    Posts
    22
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Multithreaded loading of imaes with QRunnable

    I would also recommend to use a dedicated threadpool unless you are sure you are not needing the global instance anywhere else.
    Which disadvantage would result from re-using the global instance somewhere else?

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

    Default Re: Multithreaded loading of imaes with QRunnable

    Quote Originally Posted by donelron View Post
    Which disadvantage would result from re-using the global instance somewhere else?
    These runnables here are I/O bound and would potentially block runnables from other use cases.

    A dedicated threadpool could even be restricted to fewer threads as to not cause to many parallel I/O operations.
    E.g. the software might run on a system with traditional harddisks not with SSDs.

    Cheers,
    _

  9. #9
    Join Date
    Jul 2015
    Posts
    22
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Multithreaded loading of imaes with QRunnable

    @anda_skoa:
    I've been working on this a bit more and it seems that my minimal working example does not cover all the pitfalls of the "real code". The things that made it crash originate somwhere else.
    So, I ended up, basically using it like I first posted, and included some of the modifications you proposed. Thanks once again!

Similar Threads

  1. get status of QRunnable
    By Qtonimo in forum Qt Programming
    Replies: 6
    Last Post: 30th July 2012, 08:13
  2. Use QRunnable without QThreadPool
    By Qiieha in forum Qt Programming
    Replies: 2
    Last Post: 18th August 2011, 10:02
  3. How to emit signals from QRunnable?
    By mlheese in forum Newbie
    Replies: 1
    Last Post: 29th July 2010, 23:26
  4. QRunnable - how to cancel execution?
    By TorAn in forum Qt Programming
    Replies: 1
    Last Post: 3rd March 2010, 19:11
  5. QThreadPool and QRunnable
    By jimc1200 in forum Qt Programming
    Replies: 3
    Last Post: 6th May 2009, 10:43

Tags for this Thread

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.