Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Can anybody explain the Qt::BlockingQueuedConnection with example ?
Forum Updated to NodeBB v4.3 + New Features

Can anybody explain the Qt::BlockingQueuedConnection with example ?

Scheduled Pinned Locked Moved Solved General and Desktop
18 Posts 7 Posters 5.6k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Christian EhrlicherC Christian Ehrlicher

    @Qt-embedded-developer said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

    Because i am not understanding when to use Qt::BlockingQueuedConnection .

    Then you should not use it.

    BlockingQueuedConnection should not be used since it does block the current thread until the slots were executed which is mostly a bad design.

    Q Offline
    Q Offline
    Qt embedded developer
    wrote on last edited by
    #3

    @Christian-Ehrlicher then why it get introduced ?

    if any thing is bad then we must not introduced in programming language.

    if it introduced for any good reason then let me know with real time example (situation)

    Christian EhrlicherC 1 Reply Last reply
    0
    • Q Qt embedded developer

      I want to understand the use of Qt::BlockingQueuedConnection with example.

      Because i am not understanding when to use Qt::BlockingQueuedConnection .

      i need real time application of it also

      SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #4

      Hi,

      The only use case is if you need to wait on a slot in a different thread to be done.

      This also means that you are blocking the calling thread hence, don't use it unless it's really the only and last possible solution.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      Q 1 Reply Last reply
      2
      • Q Qt embedded developer

        @Christian-Ehrlicher then why it get introduced ?

        if any thing is bad then we must not introduced in programming language.

        if it introduced for any good reason then let me know with real time example (situation)

        Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #5

        @Qt-embedded-developer said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

        if any thing is bad then we must not introduced in programming language

        It was not introduced in a programming language, it's just a functionality in a library.
        And just because I said it should not be used doesn't mean that there are corner cases where it is needed - otherwise it would not have been added to the library.
        Download the Qt source code, search for BlockingQueuedConnection and you will see the corner cases.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        3
        • SGaistS SGaist

          Hi,

          The only use case is if you need to wait on a slot in a different thread to be done.

          This also means that you are blocking the calling thread hence, don't use it unless it's really the only and last possible solution.

          Q Offline
          Q Offline
          Qt embedded developer
          wrote on last edited by
          #6

          @SGaist said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

          The only use case is if you need to wait on a slot in a different thread to be done.

          Can you elaborate this with real time application or example.

          Christian EhrlicherC 1 Reply Last reply
          0
          • Q Qt embedded developer

            @SGaist said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

            The only use case is if you need to wait on a slot in a different thread to be done.

            Can you elaborate this with real time application or example.

            Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #7

            @Qt-embedded-developer said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

            Can you elaborate this with real time application or example.

            Download the Qt source code, search for BlockingQueuedConnection and you will see the corner cases.

            Isn't this enough?

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            Q 1 Reply Last reply
            0
            • Christian EhrlicherC Christian Ehrlicher

              @Qt-embedded-developer said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

              Can you elaborate this with real time application or example.

              Download the Qt source code, search for BlockingQueuedConnection and you will see the corner cases.

              Isn't this enough?

              Q Offline
              Q Offline
              Qt embedded developer
              wrote on last edited by
              #8

              @Christian-Ehrlicher said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

              BlockingQueuedConnection

              I found below things from source code :

              Qt::BlockingQueuedConnection, the method will be invoked in the same way as for Qt::QueuedConnection, except that the current thread will block until the event is delivered. Using this connection type to communicate between objects in the same thread will lead to deadlocks.

              Christian EhrlicherC 1 Reply Last reply
              0
              • Q Qt embedded developer

                @Christian-Ehrlicher said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                BlockingQueuedConnection

                I found below things from source code :

                Qt::BlockingQueuedConnection, the method will be invoked in the same way as for Qt::QueuedConnection, except that the current thread will block until the event is delivered. Using this connection type to communicate between objects in the same thread will lead to deadlocks.

                Christian EhrlicherC Offline
                Christian EhrlicherC Offline
                Christian Ehrlicher
                Lifetime Qt Champion
                wrote on last edited by
                #9

                You found the documentation - good start.

                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                Visit the Qt Academy at https://academy.qt.io/catalog

                1 Reply Last reply
                2
                • Chris KawaC Offline
                  Chris KawaC Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on last edited by Chris Kawa
                  #10

                  There are 4 types of connections:

                  Direct:
                  direct connection

                  Queued:
                  queued connection

                  Blocked queued:
                  blocked queued connection

                  Auto: same as Direct when sender and receiver are on the same thread and Queued when they're on different threads.

                  It's hard to come up with a good example for the blocking queued connection use because there's rarely any. It blocks the sender thread, so it could be used as synchronization mechanism. A very contrived example would be when a thread produces a set of data to be worked on, then emits a "start work" signal, a bunch of other threads pick up the work with a slot and the producing thread waits until they're done. As I said that's kinda forced, because you would usually handle this without blocking. As others said, if you can't think of a reason to use this type of connection it means you don't have a reason to use it.

                  I've been using Qt for over a decade and I don't think I ever found a good reason to use it, only a couple of bad ones that got refactored out later on.

                  1 Reply Last reply
                  9
                  • Q Qt embedded developer has marked this topic as solved on
                  • oblivioncthO Offline
                    oblivioncthO Offline
                    oblivioncth
                    wrote on last edited by
                    #11

                    Just wanted to add one real example of using a BlockingQueuedConnection: Error handling. While ideally you can design around errors such that more work can be performed even when they occur, sometimes you can't. This also may apply for events that aren't strictly errors, but impose similar "blocking" situations.

                    Let's assume you're using the preferred worker-object multithreading model so that your worker object can perform a task in a new thread without blocking the one it spawns it. I'll further assume the most common case for that here, in which the spawning thread is the main/UI thread and you're trying to perform a long task without blocking the UI and/or other small tasks from processing there.

                    The QThread docs have a good example of this.

                    Now, let's group the kind of error's that might occur during this work into reasonable categories:

                    1. A simple error the user can do nothing about
                    2. An error that requires a choice to handle but doesn't impede other work
                    3. An error of a particular nature such that the worker thread's logic must fork entirely based on a decision, or potentially outright abandon its task.

                    The first two can be handled asynchronously, but the third type is by nature blocking and where Qt::BlockingQueuedConnection is useful.

                    Expanding on the QThread example, you might end up with something like this:

                    #include <QMessageBox>
                    #include <QInputDialog>
                    #include <QThread>
                    #include <QFile>
                    
                    class Worker : public QObject
                    {
                        Q_OBJECT
                    
                    public slots:
                        void doWork() {
                            // ... here is the expensive or blocking operation
                    
                            /* Try to copy a file. If it fails, we need to prompt the user for a response,
                             * but we don't need it right away
                             */
                            if(!QFile::copy("/source/path", "/dest/path"))
                                emit actionableErrorOccurred("File copy failed");
                            // ... more work
                    
                            /* Try to remove a file. If it fails, the user should know, but there's nothing
                             * to be done
                             */
                            if(!QFile::remove("/delete/me"))
                                emit errorOccurred("File deletion failed");
                            // ... more work
                    
                            /* Check if an imperative file exists. Highly unexpected to be missing, so if it
                             * it is, then it might not make sense to continue and we need to know what the
                             * user want's to do before proceeding.
                             */
                            bool abort = false;
                            emit blockingErrorOccurred(&abort, "Critical file missing, abort?");
                            if(abort)
                            {
                                emit workFinished("Failed - User aborted");
                                return;
                            }
                            // ... more work
                    
                            emit workFinished("Success");
                        }
                        void handleActionResponse(const QString& response) { ... } // Handle earlier choices here
                    
                    signals:
                        void workFinished(const QString& result);
                        void errorOccurred(const QString& msg);
                        void actionableErrorOccurred(const QString& msg);
                        void blockingErrorOccurred(bool* response, const QString& msg);
                    };
                    
                    class Controller : public QObject
                    {
                        Q_OBJECT
                        QThread workerThread;
                    public:
                        Controller(QObject* parent) :
                            QObject(parent)
                        {
                            /* NOTE: Connections made to "worker" here without a connection type specified are
                             * all Qt::QueuedConnection since it lives in another thread.
                             */
                    
                            // Create worker
                            Worker* worker = new Worker;
                            worker->moveToThread(&workerThread);
                    
                            // Operation
                            connect(worker, &Worker::workFinished, this, &Controller::handleResult);
                            connect(worker, &Worker::workFinished, &workerThread, &QThread::quit);
                            connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
                            connect(this, &Controller::operate, worker, &Worker::doWork);
                    
                            // Error handling
                            connect(worker, &Worker::errorOccurred, this, &Controller::handleError);
                            connect(worker, &Worker::actionableErrorOccurred, this, &Controller::handleActionableError);
                            connect(this, &Controller::actionSelected, worker, &Worker::handleActionResponse);
                            connect(worker, &Worker::blockingErrorOccurred, this, &Controller::handleBlockingError, Qt::BlockingQueuedConnection);
                            /* Specifying Qt::BlockingConnection on this last connection makes the worker
                             * object appropriately block when it encounters an error that requires immediate
                             * user input to handle
                             */
                    
                            // Start thread
                            workerThread.start();
                        }
                        ~Controller() {
                            workerThread.quit();
                            workerThread.wait();
                        }
                    
                        void startWork() { emit operate(); }
                    
                    private slots:
                        void handleResult(const QString& result) { ... } // Handle result
                        void handleError(const QString& msg) { QMessageBox::warning(nullptr, "Issue", msg); }
                        void handleActionableError(const QString& msg)
                        {
                            static const QStringList options{"Option 1", "Option 2", "Option 3"};
                            QString action = QInputDialog::getItem(nullptr, "Actionable Issue", msg, options);
                            emit actionSelected(action);
                        }
                        void handleBlockingError(bool* response, const QString& msg)
                        {
                            Q_ASSERT(response);
                            auto button = QMessageBox::critical(nullptr, "Major Error", msg, QMessageBox::Yes | QMessageBox::No);
                            *response = button == QMessageBox::Yes;
                        }
                    
                    signals:
                        void operate();
                        void actionSelected(const QString& action);
                    };
                    
                    MainWindow::doLongOperation()
                    {
                        Controller* workController = new Controller(this);
                        workController->startWork();
                    }
                    
                    #include "main.moc"
                    
                    
                    

                    If the worker in the worker thread runs into a situation where it's unsure on how to proceed without a choice being made, it can essentially request a response from the UI thread for user input on how to handle the situation and wait for the response, thanks to Qt::BlockingQueuedConnection.

                    Of course this example is heavily cut down, but it's just to give an idea of the use case. You should always try to design execution flow to avoid blocking whenever possible, but that isn't achievable 100% of the time.

                    S 1 Reply Last reply
                    0
                    • oblivioncthO oblivioncth

                      Just wanted to add one real example of using a BlockingQueuedConnection: Error handling. While ideally you can design around errors such that more work can be performed even when they occur, sometimes you can't. This also may apply for events that aren't strictly errors, but impose similar "blocking" situations.

                      Let's assume you're using the preferred worker-object multithreading model so that your worker object can perform a task in a new thread without blocking the one it spawns it. I'll further assume the most common case for that here, in which the spawning thread is the main/UI thread and you're trying to perform a long task without blocking the UI and/or other small tasks from processing there.

                      The QThread docs have a good example of this.

                      Now, let's group the kind of error's that might occur during this work into reasonable categories:

                      1. A simple error the user can do nothing about
                      2. An error that requires a choice to handle but doesn't impede other work
                      3. An error of a particular nature such that the worker thread's logic must fork entirely based on a decision, or potentially outright abandon its task.

                      The first two can be handled asynchronously, but the third type is by nature blocking and where Qt::BlockingQueuedConnection is useful.

                      Expanding on the QThread example, you might end up with something like this:

                      #include <QMessageBox>
                      #include <QInputDialog>
                      #include <QThread>
                      #include <QFile>
                      
                      class Worker : public QObject
                      {
                          Q_OBJECT
                      
                      public slots:
                          void doWork() {
                              // ... here is the expensive or blocking operation
                      
                              /* Try to copy a file. If it fails, we need to prompt the user for a response,
                               * but we don't need it right away
                               */
                              if(!QFile::copy("/source/path", "/dest/path"))
                                  emit actionableErrorOccurred("File copy failed");
                              // ... more work
                      
                              /* Try to remove a file. If it fails, the user should know, but there's nothing
                               * to be done
                               */
                              if(!QFile::remove("/delete/me"))
                                  emit errorOccurred("File deletion failed");
                              // ... more work
                      
                              /* Check if an imperative file exists. Highly unexpected to be missing, so if it
                               * it is, then it might not make sense to continue and we need to know what the
                               * user want's to do before proceeding.
                               */
                              bool abort = false;
                              emit blockingErrorOccurred(&abort, "Critical file missing, abort?");
                              if(abort)
                              {
                                  emit workFinished("Failed - User aborted");
                                  return;
                              }
                              // ... more work
                      
                              emit workFinished("Success");
                          }
                          void handleActionResponse(const QString& response) { ... } // Handle earlier choices here
                      
                      signals:
                          void workFinished(const QString& result);
                          void errorOccurred(const QString& msg);
                          void actionableErrorOccurred(const QString& msg);
                          void blockingErrorOccurred(bool* response, const QString& msg);
                      };
                      
                      class Controller : public QObject
                      {
                          Q_OBJECT
                          QThread workerThread;
                      public:
                          Controller(QObject* parent) :
                              QObject(parent)
                          {
                              /* NOTE: Connections made to "worker" here without a connection type specified are
                               * all Qt::QueuedConnection since it lives in another thread.
                               */
                      
                              // Create worker
                              Worker* worker = new Worker;
                              worker->moveToThread(&workerThread);
                      
                              // Operation
                              connect(worker, &Worker::workFinished, this, &Controller::handleResult);
                              connect(worker, &Worker::workFinished, &workerThread, &QThread::quit);
                              connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
                              connect(this, &Controller::operate, worker, &Worker::doWork);
                      
                              // Error handling
                              connect(worker, &Worker::errorOccurred, this, &Controller::handleError);
                              connect(worker, &Worker::actionableErrorOccurred, this, &Controller::handleActionableError);
                              connect(this, &Controller::actionSelected, worker, &Worker::handleActionResponse);
                              connect(worker, &Worker::blockingErrorOccurred, this, &Controller::handleBlockingError, Qt::BlockingQueuedConnection);
                              /* Specifying Qt::BlockingConnection on this last connection makes the worker
                               * object appropriately block when it encounters an error that requires immediate
                               * user input to handle
                               */
                      
                              // Start thread
                              workerThread.start();
                          }
                          ~Controller() {
                              workerThread.quit();
                              workerThread.wait();
                          }
                      
                          void startWork() { emit operate(); }
                      
                      private slots:
                          void handleResult(const QString& result) { ... } // Handle result
                          void handleError(const QString& msg) { QMessageBox::warning(nullptr, "Issue", msg); }
                          void handleActionableError(const QString& msg)
                          {
                              static const QStringList options{"Option 1", "Option 2", "Option 3"};
                              QString action = QInputDialog::getItem(nullptr, "Actionable Issue", msg, options);
                              emit actionSelected(action);
                          }
                          void handleBlockingError(bool* response, const QString& msg)
                          {
                              Q_ASSERT(response);
                              auto button = QMessageBox::critical(nullptr, "Major Error", msg, QMessageBox::Yes | QMessageBox::No);
                              *response = button == QMessageBox::Yes;
                          }
                      
                      signals:
                          void operate();
                          void actionSelected(const QString& action);
                      };
                      
                      MainWindow::doLongOperation()
                      {
                          Controller* workController = new Controller(this);
                          workController->startWork();
                      }
                      
                      #include "main.moc"
                      
                      
                      

                      If the worker in the worker thread runs into a situation where it's unsure on how to proceed without a choice being made, it can essentially request a response from the UI thread for user input on how to handle the situation and wait for the response, thanks to Qt::BlockingQueuedConnection.

                      Of course this example is heavily cut down, but it's just to give an idea of the use case. You should always try to design execution flow to avoid blocking whenever possible, but that isn't achievable 100% of the time.

                      S Offline
                      S Offline
                      SimonSchroeder
                      wrote on last edited by
                      #12

                      @oblivioncth said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                      The first two can be handled asynchronously, but the third type is by nature blocking and where Qt::BlockingQueuedConnection is useful.
                      Expanding on the QThread example, you might end up with something like this:

                      Well, it is certainly the most easy way to reason about things. However, it can actually be solved asynchronously. A general advice would be to split up the work into several chunks with individual slots. One of the main points of object oriented programming is that objects can store state. This would mean that you would have a new slot:

                      void doWorkPart2(bool abort)
                      {
                          if(abort)
                          {
                              emit workFinished("Failed - User aborted");
                              return;
                          }
                          // ... more work
                      
                          emit workFinished("Success");
                      }
                      

                      blockingErrorOccured should trigger a slot which in itself will trigger a signal that doWorkPart2 could be connected to.

                      I do understand that this disrupts the flow of the algorithm. However, you could use a lambda directly inplace instead if you work out how to connect the signal carrying the abort to the lambda. This would keep the logic local.

                      PS: To be honest, we migrated some old software over to Qt. In order to not have to rewrite everything (because who has the time and money?) we wrote a little wrapper guiThread(...) where we could just place the code for the dialog (not a separate slot to be triggered with blockingErrorOccured). So, in our case even the GUI code is in situ (using a lambda and QMetaObject::invokeMethod under the hood).

                      oblivioncthO 1 Reply Last reply
                      1
                      • S SimonSchroeder

                        @oblivioncth said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                        The first two can be handled asynchronously, but the third type is by nature blocking and where Qt::BlockingQueuedConnection is useful.
                        Expanding on the QThread example, you might end up with something like this:

                        Well, it is certainly the most easy way to reason about things. However, it can actually be solved asynchronously. A general advice would be to split up the work into several chunks with individual slots. One of the main points of object oriented programming is that objects can store state. This would mean that you would have a new slot:

                        void doWorkPart2(bool abort)
                        {
                            if(abort)
                            {
                                emit workFinished("Failed - User aborted");
                                return;
                            }
                            // ... more work
                        
                            emit workFinished("Success");
                        }
                        

                        blockingErrorOccured should trigger a slot which in itself will trigger a signal that doWorkPart2 could be connected to.

                        I do understand that this disrupts the flow of the algorithm. However, you could use a lambda directly inplace instead if you work out how to connect the signal carrying the abort to the lambda. This would keep the logic local.

                        PS: To be honest, we migrated some old software over to Qt. In order to not have to rewrite everything (because who has the time and money?) we wrote a little wrapper guiThread(...) where we could just place the code for the dialog (not a separate slot to be triggered with blockingErrorOccured). So, in our case even the GUI code is in situ (using a lambda and QMetaObject::invokeMethod under the hood).

                        oblivioncthO Offline
                        oblivioncthO Offline
                        oblivioncth
                        wrote on last edited by
                        #13

                        @SimonSchroeder said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                        Well, it is certainly the most easy way to reason about things. However, it can actually be solved asynchronously. A general advice would be to split up the work into several chunks with individual slots. One of the main points of object oriented programming is that objects can store state. This would mean that you would have a new slot:

                        In the spirit of never say never, perhaps I shouldn't have stated "there are situations where this must be used" and rather, "there are situations where this is reasonable. I do still think there are specific cases where it is nearly outright required, but yes, those are extremely rare.

                        Assuming I understand you correctly:

                        // In controller
                        private slots:
                            handlePartOneResult(Result r)
                           {
                               bool abort = false;
                               if(Result == Result::BlockingError)
                                   abort= ... // Ask user if they want to continue via msg or something
                        
                              emit startPartTwo(abort);
                           }
                           handlePartTwoResult(Result r) { ... }
                        
                        // In worker
                        public slots:
                            void doPartOne()
                            {
                                ...
                                return Result::...
                            }
                            void doPartTwo(bool abort)
                            {
                                 if(abort)
                                    return;
                                 ...  
                            }
                        

                        Yes, this is technically better if the focus is to absolutely keep the event loop in the worker thread unblocked no matter what, which to be fair is a good goal is most cases; however, as you mentioned yourself this can eventually come at the cost of readability/complexity since you have to break up your code at every point where a decision fork that requires talking to the UI thread needs to be made, which could get quite messy (yet of course sometimes you just deal with this anyway).

                        In this topic there are really two types of "blocking" we're talking about here: Execution blocking (generally the default meaning), and "decision blocking", logic blocking, or whatever you want to call it. I more so was referring to the latter when I said "sometimes you're stuck in a blocking situation"; situations like dependent steps in a CI/CD workflow. Remember, this is a worker thread, and in these situations you might not be able to do any more work at all before getting the answer. So, while you can structure the thread like you suggest and avoid blocking the event loop... what are you blocking it from? If your worker thread also receives events from other places and takes care of other, more independent tasks then by all means you need to keep that loop spinning, as would be the case in more complex designs; however, if this is just a simple worker thread dedicated to handling a mostly interdependent pipeline of tightly related tasks and at some point it becomes "logically blocked", I don't think it's unreasonable to simply block execution while waiting for a response. In this scenario the thread can't do anything else anyway.

                        At least for me, most of the time I add a second thread (ignoring the additional ones you're already going to have due to system libraries and the like), I'm probably adding a third and maybe more. Usually I'll have one thread (whether the default main/UI thread or a new primary thread) that never ever blocks and handles event processing while keeping work tight so that loop is as free as possible. This thread will then dispatch work as needed to one or more dedicated worker threads that tend to be much more linear as I described earlier. So in the rare event those need sudden unexpected intervention, blocking their execution is generally a non-issue and keeps things cleaner. Additionally, the dispatcher thread itself can intercept the requests instead of forwarding them and override the return-via-parameter value quickly should the response have actually been determined earlier, or determined to be no longer needed. Perhaps this has just worked out for me since a large number of my projects have involved at least some sections that are very linear, yet long enough to constitute being in a separate thread. There are many types of applications where this approach would be a non-starter pretty much always.

                        To be clear though, I suppose it is important to encourage people newer to the event loop and threads to almost always prefer non-blocking solutions, especially in the main thread.

                        @SimonSchroeder said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                        However, you could use a lambda directly

                        This would be much more like using Javascipt's Promises (which Qt does have an equivalent too that I've never messed with), which I do think is a better happy medium than potential slot madness if there would be a high number, though you can potentially end up with a lot nested lambdas which isn't great either.

                        By no means do I claim to be an expert in handling multi-threaded applications, I'm sure there are many things I could have better parallelized but didn't, I just wanted to say that I think in some situations Qt::BlockingQueuedConnections has a good use :)

                        S 1 Reply Last reply
                        0
                        • oblivioncthO oblivioncth

                          @SimonSchroeder said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                          Well, it is certainly the most easy way to reason about things. However, it can actually be solved asynchronously. A general advice would be to split up the work into several chunks with individual slots. One of the main points of object oriented programming is that objects can store state. This would mean that you would have a new slot:

                          In the spirit of never say never, perhaps I shouldn't have stated "there are situations where this must be used" and rather, "there are situations where this is reasonable. I do still think there are specific cases where it is nearly outright required, but yes, those are extremely rare.

                          Assuming I understand you correctly:

                          // In controller
                          private slots:
                              handlePartOneResult(Result r)
                             {
                                 bool abort = false;
                                 if(Result == Result::BlockingError)
                                     abort= ... // Ask user if they want to continue via msg or something
                          
                                emit startPartTwo(abort);
                             }
                             handlePartTwoResult(Result r) { ... }
                          
                          // In worker
                          public slots:
                              void doPartOne()
                              {
                                  ...
                                  return Result::...
                              }
                              void doPartTwo(bool abort)
                              {
                                   if(abort)
                                      return;
                                   ...  
                              }
                          

                          Yes, this is technically better if the focus is to absolutely keep the event loop in the worker thread unblocked no matter what, which to be fair is a good goal is most cases; however, as you mentioned yourself this can eventually come at the cost of readability/complexity since you have to break up your code at every point where a decision fork that requires talking to the UI thread needs to be made, which could get quite messy (yet of course sometimes you just deal with this anyway).

                          In this topic there are really two types of "blocking" we're talking about here: Execution blocking (generally the default meaning), and "decision blocking", logic blocking, or whatever you want to call it. I more so was referring to the latter when I said "sometimes you're stuck in a blocking situation"; situations like dependent steps in a CI/CD workflow. Remember, this is a worker thread, and in these situations you might not be able to do any more work at all before getting the answer. So, while you can structure the thread like you suggest and avoid blocking the event loop... what are you blocking it from? If your worker thread also receives events from other places and takes care of other, more independent tasks then by all means you need to keep that loop spinning, as would be the case in more complex designs; however, if this is just a simple worker thread dedicated to handling a mostly interdependent pipeline of tightly related tasks and at some point it becomes "logically blocked", I don't think it's unreasonable to simply block execution while waiting for a response. In this scenario the thread can't do anything else anyway.

                          At least for me, most of the time I add a second thread (ignoring the additional ones you're already going to have due to system libraries and the like), I'm probably adding a third and maybe more. Usually I'll have one thread (whether the default main/UI thread or a new primary thread) that never ever blocks and handles event processing while keeping work tight so that loop is as free as possible. This thread will then dispatch work as needed to one or more dedicated worker threads that tend to be much more linear as I described earlier. So in the rare event those need sudden unexpected intervention, blocking their execution is generally a non-issue and keeps things cleaner. Additionally, the dispatcher thread itself can intercept the requests instead of forwarding them and override the return-via-parameter value quickly should the response have actually been determined earlier, or determined to be no longer needed. Perhaps this has just worked out for me since a large number of my projects have involved at least some sections that are very linear, yet long enough to constitute being in a separate thread. There are many types of applications where this approach would be a non-starter pretty much always.

                          To be clear though, I suppose it is important to encourage people newer to the event loop and threads to almost always prefer non-blocking solutions, especially in the main thread.

                          @SimonSchroeder said in Can anybody explain the Qt::BlockingQueuedConnection with example ?:

                          However, you could use a lambda directly

                          This would be much more like using Javascipt's Promises (which Qt does have an equivalent too that I've never messed with), which I do think is a better happy medium than potential slot madness if there would be a high number, though you can potentially end up with a lot nested lambdas which isn't great either.

                          By no means do I claim to be an expert in handling multi-threaded applications, I'm sure there are many things I could have better parallelized but didn't, I just wanted to say that I think in some situations Qt::BlockingQueuedConnections has a good use :)

                          S Offline
                          S Offline
                          SimonSchroeder
                          wrote on last edited by
                          #14

                          @oblivioncth I completely agree with what you are saying. It would be nice for the future if we could use coroutines in these contexts. Just a simple co_yield to pause execution in the worker thread while waiting for the GUI and then resuming just at that same point in the code. One can dream...

                          oblivioncthO 1 Reply Last reply
                          1
                          • S SimonSchroeder

                            @oblivioncth I completely agree with what you are saying. It would be nice for the future if we could use coroutines in these contexts. Just a simple co_yield to pause execution in the worker thread while waiting for the GUI and then resuming just at that same point in the code. One can dream...

                            oblivioncthO Offline
                            oblivioncthO Offline
                            oblivioncth
                            wrote on last edited by
                            #15

                            @SimonSchroeder

                            Hmmm... on that note I imagine I could come up with something where the initial doWork() slot manages a queue of of coroutine handles/requests so that when I normally emit with a blocking queued connection in some long function I could instead co_yield a pointer to a value I want set, with the doWork() function then storing the coroutine resume handle in a FIFO queue and emitting a non-blocking signal to to the controller in the other thread which can then respond back with the desired value in an also non-blocking manner. The responding slot then pulls the corresponding request out of the queue, dereferences the pointer and sets it to the passed value, and then uses the handle to resume the yielded function.

                            This would still allow logical blocking when it makes sense while keeping the event loop open for when I would need that thread handling other events. I do like the sounds of this. It would be a good reason to experiment with C++20 coroutines, which (along with ranges to a degree) are one of its features I haven't really used.

                            It doesn't solve the most annoying case which is having to regularly check (by yielding or checking a flag) in the long function if an abort request has come from the outside, but that's just how it is. That's where splitting up work into really tiny chunks that ideally can go in a queue where the event loop is returned to after each is perfect if you can do it.

                            But yea, I hope that coroutines in general are expanded upon and eventually Qt implements some slick integrations with them. Although it may be a pipe dream, being able to co_yield directly to the event loop, while maybe even emitting a signal, would be awesome.

                            1 Reply Last reply
                            0
                            • M Offline
                              M Offline
                              mdrost
                              wrote on last edited by
                              #16

                              A side question:
                              When sender and receiver are in the same thread, why Qt::BlockingQueuedConnection can't behave as Qt::DirectConnection instead of deadlocking?

                              Chris KawaC S 2 Replies Last reply
                              0
                              • M mdrost

                                A side question:
                                When sender and receiver are in the same thread, why Qt::BlockingQueuedConnection can't behave as Qt::DirectConnection instead of deadlocking?

                                Chris KawaC Offline
                                Chris KawaC Offline
                                Chris Kawa
                                Lifetime Qt Champion
                                wrote on last edited by
                                #17

                                @mdrost Because it has Queued in the name, so it is put in a queue.

                                1 Reply Last reply
                                2
                                • M mdrost

                                  A side question:
                                  When sender and receiver are in the same thread, why Qt::BlockingQueuedConnection can't behave as Qt::DirectConnection instead of deadlocking?

                                  S Offline
                                  S Offline
                                  SimonSchroeder
                                  wrote on last edited by
                                  #18

                                  @mdrost And a DirectConnection is by definition always blocking.

                                  1 Reply Last reply
                                  0

                                  • Login

                                  • Login or register to search.
                                  • First post
                                    Last post
                                  0
                                  • Categories
                                  • Recent
                                  • Tags
                                  • Popular
                                  • Users
                                  • Groups
                                  • Search
                                  • Get Qt Extensions
                                  • Unsolved