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. Update UI from multiple threads

Update UI from multiple threads

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 4 Posters 265 Views
  • 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.
  • C Offline
    C Offline
    Christian Ehrlicher
    Lifetime Qt Champion
    wrote 21 days ago last edited by Christian Ehrlicher
    #5

    But you use the wrong context - it must be an object in the main thread. Fix you connect.

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

    A 1 Reply Last reply 21 days ago
    1
    • C Christian Ehrlicher
      21 days ago

      But you use the wrong context - it must be an object in the main thread. Fix you connect.

      A Offline
      A Offline
      aiphae
      wrote 21 days ago last edited by
      #6

      @Christian-Ehrlicher said in Update UI from multiple threads:

      But you use the wrong context - it must be an object in the main thread. Fix you connect.

      The connecting itself happens in the main thread. What do you mean?

      C 1 Reply Last reply 21 days ago
      0
      • A aiphae
        21 days ago

        @Christian-Ehrlicher said in Update UI from multiple threads:

        But you use the wrong context - it must be an object in the main thread. Fix you connect.

        The connecting itself happens in the main thread. What do you mean?

        C Offline
        C Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote 21 days ago last edited by
        #7

        @aiphae said in Update UI from multiple threads:

        What do you mean?

        Where does 'this' lives in? Not in the main thread I would guess (but to few code)

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

        A 1 Reply Last reply 21 days ago
        0
        • C Christian Ehrlicher
          21 days ago

          @aiphae said in Update UI from multiple threads:

          What do you mean?

          Where does 'this' lives in? Not in the main thread I would guess (but to few code)

          A Offline
          A Offline
          aiphae
          wrote 21 days ago last edited by
          #8

          @Christian-Ehrlicher 'this' is a widget class. It lives in the main thread.

          I found the issue: getMatAtFrame() loads images from a disk and it is slower than actual processing. I'll need some different concurrency structute.

          J 1 Reply Last reply 21 days ago
          0
          • A aiphae has marked this topic as solved 21 days ago
          • A aiphae
            21 days ago

            @Christian-Ehrlicher 'this' is a widget class. It lives in the main thread.

            I found the issue: getMatAtFrame() loads images from a disk and it is slower than actual processing. I'll need some different concurrency structute.

            J Offline
            J Offline
            JonB
            wrote 21 days ago last edited by JonB
            #9

            @aiphae
            I don't see how this would cause your reported

            However, the progress text does not change in real time, and after the function completes, the final number does not match totalFrames

            It should still change in "real time" for whatever speed it manages, and the total ought be correct....

            And if @Christian-Ehrlicher says you have the wrong context for a thread connection I would also believe him....

            A 1 Reply Last reply 20 days ago
            0
            • A aiphae has marked this topic as unsolved 20 days ago
            • J JonB
              21 days ago

              @aiphae
              I don't see how this would cause your reported

              However, the progress text does not change in real time, and after the function completes, the final number does not match totalFrames

              It should still change in "real time" for whatever speed it manages, and the total ought be correct....

              And if @Christian-Ehrlicher says you have the wrong context for a thread connection I would also believe him....

              A Offline
              A Offline
              aiphae
              wrote 20 days ago last edited by
              #10

              @JonB

              You were right.

              Here's the minimum example of the program (mainwindow.h and mainwindow.cpp) :

              #ifndef MAINWINDOW_H
              #define MAINWINDOW_H
              
              #include <QMainWindow>
              
              #include <vector>
              #include <thread>
              #include <queue>
              #include <mutex>
              #include <condition_variable>
              #include <functional>
              #include <future>
              
              class ThreadPool {
              public:
                  explicit ThreadPool(int threadNumber) {
                      for (int i = 0; i < threadNumber; ++i) {
                          workers.emplace_back([this] {
                              for (;;) {
                                  std::function<void()> task;
                                  {
                                      std::unique_lock<std::mutex> lock(queueMutex);
                                      condition.wait(lock, [this] { return stop || !tasks.empty(); });
                                      if (stop && tasks.empty()) {
                                          return;
                                      }
                                      task = std::move(tasks.front());
                                      tasks.pop();
                                  }
                                  task();
                              }
                          });
                      }
                  }
                  ~ThreadPool() {
                      {
                          std::unique_lock<std::mutex> lock(queueMutex);
                          stop = true;
                      }
                      condition.notify_all();
                      for (auto &worker : workers) {
                          worker.join();
                      }
                  }
              
                  template<typename F, typename... Args>
                  auto enqueue(F&& f, Args&&... args)
                      -> std::future<typename std::invoke_result<F, Args...>::type> {
                      using return_type = typename std::invoke_result<F, Args...>::type;
              
                      auto task = std::make_shared<std::packaged_task<return_type()>>(
                          std::bind(std::forward<F>(f), std::forward<Args>(args)...)
                          );
              
                      std::future<return_type> res = task->get_future();
                      {
                          std::unique_lock<std::mutex> lock(queueMutex);
                          if (stop) {
                              throw std::runtime_error("enqueue on stopped ThreadPool");
                          }
                          tasks.emplace([task]() { (*task)(); });
                      }
                      condition.notify_one();
                      return res;
                  }
              
              private:
                  std::vector<std::thread> workers;
                  std::queue<std::function<void()>> tasks;
              
                  std::mutex queueMutex;
                  std::condition_variable condition;
                  bool stop = false;
              };
              
              QT_BEGIN_NAMESPACE
              namespace Ui {
              class MainWindow;
              }
              QT_END_NAMESPACE
              
              class MainWindow : public QMainWindow
              {
                  Q_OBJECT
              
              public:
                  MainWindow(QWidget *parent = nullptr);
                  ~MainWindow();
              
              signals:
                  void update(int value);
              
              private:
                  Ui::MainWindow *ui;
                  void start();
                  std::atomic<int> total = 0;
              };
              #endif // MAINWINDOW_H
              
              #include "mainwindow.h"
              #include "ui_mainwindow.h"
              
              MainWindow::MainWindow(QWidget *parent)
                  : QMainWindow(parent)
                  , ui(new Ui::MainWindow)
              {
                  ui->setupUi(this);
                  connect(this, &MainWindow::update, this, [](int value) {
                      qDebug() << value;
                  });
                  start();
              }
              
              MainWindow::~MainWindow()
              {
                  delete ui;
              }
              
              void MainWindow::start() {
                  ThreadPool pool(std::thread::hardware_concurrency() - 1);
                  total = 0;
                  for (int i = 0; i < 100; ++i) {
                      pool.enqueue([this]() {
                          int value = ++total;
                          std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                          emit update(value);
                      });
                  }
              }
              
              C 1 Reply Last reply 20 days ago
              0
              • A aiphae
                20 days ago

                @JonB

                You were right.

                Here's the minimum example of the program (mainwindow.h and mainwindow.cpp) :

                #ifndef MAINWINDOW_H
                #define MAINWINDOW_H
                
                #include <QMainWindow>
                
                #include <vector>
                #include <thread>
                #include <queue>
                #include <mutex>
                #include <condition_variable>
                #include <functional>
                #include <future>
                
                class ThreadPool {
                public:
                    explicit ThreadPool(int threadNumber) {
                        for (int i = 0; i < threadNumber; ++i) {
                            workers.emplace_back([this] {
                                for (;;) {
                                    std::function<void()> task;
                                    {
                                        std::unique_lock<std::mutex> lock(queueMutex);
                                        condition.wait(lock, [this] { return stop || !tasks.empty(); });
                                        if (stop && tasks.empty()) {
                                            return;
                                        }
                                        task = std::move(tasks.front());
                                        tasks.pop();
                                    }
                                    task();
                                }
                            });
                        }
                    }
                    ~ThreadPool() {
                        {
                            std::unique_lock<std::mutex> lock(queueMutex);
                            stop = true;
                        }
                        condition.notify_all();
                        for (auto &worker : workers) {
                            worker.join();
                        }
                    }
                
                    template<typename F, typename... Args>
                    auto enqueue(F&& f, Args&&... args)
                        -> std::future<typename std::invoke_result<F, Args...>::type> {
                        using return_type = typename std::invoke_result<F, Args...>::type;
                
                        auto task = std::make_shared<std::packaged_task<return_type()>>(
                            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
                            );
                
                        std::future<return_type> res = task->get_future();
                        {
                            std::unique_lock<std::mutex> lock(queueMutex);
                            if (stop) {
                                throw std::runtime_error("enqueue on stopped ThreadPool");
                            }
                            tasks.emplace([task]() { (*task)(); });
                        }
                        condition.notify_one();
                        return res;
                    }
                
                private:
                    std::vector<std::thread> workers;
                    std::queue<std::function<void()>> tasks;
                
                    std::mutex queueMutex;
                    std::condition_variable condition;
                    bool stop = false;
                };
                
                QT_BEGIN_NAMESPACE
                namespace Ui {
                class MainWindow;
                }
                QT_END_NAMESPACE
                
                class MainWindow : public QMainWindow
                {
                    Q_OBJECT
                
                public:
                    MainWindow(QWidget *parent = nullptr);
                    ~MainWindow();
                
                signals:
                    void update(int value);
                
                private:
                    Ui::MainWindow *ui;
                    void start();
                    std::atomic<int> total = 0;
                };
                #endif // MAINWINDOW_H
                
                #include "mainwindow.h"
                #include "ui_mainwindow.h"
                
                MainWindow::MainWindow(QWidget *parent)
                    : QMainWindow(parent)
                    , ui(new Ui::MainWindow)
                {
                    ui->setupUi(this);
                    connect(this, &MainWindow::update, this, [](int value) {
                        qDebug() << value;
                    });
                    start();
                }
                
                MainWindow::~MainWindow()
                {
                    delete ui;
                }
                
                void MainWindow::start() {
                    ThreadPool pool(std::thread::hardware_concurrency() - 1);
                    total = 0;
                    for (int i = 0; i < 100; ++i) {
                        pool.enqueue([this]() {
                            int value = ++total;
                            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                            emit update(value);
                        });
                    }
                }
                
                C Offline
                C Offline
                Christian Ehrlicher
                Lifetime Qt Champion
                wrote 20 days ago last edited by Christian Ehrlicher
                #11

                @aiphae said in Update UI from multiple threads:

                void MainWindow::start() {
                    ThreadPool pool(std::thread::hardware_concurrency() - 1);
                    ....
                }
                
                ~ThreadPool() {
                    {
                        std::unique_lock<std::mutex> lock(queueMutex);
                        stop = true;
                    }
                    condition.notify_all();
                    for (auto &worker : workers) {
                        worker.join();
                    }
                }
                
                

                What do you expect? You block the main event loop in the dtor.

                /edit:
                My Widget I used so it's a fully functional example which does not block:

                class MyWidget : public QLabel
                {
                    Q_OBJECT
                public:
                    MyWidget()
                        : pool(std::thread::hardware_concurrency() - 1)
                    {
                        connect(this, &MyWidget::updateValue, this, [this](int value) {
                            setText(QString::number(value));
                            });
                    }
                    void start() {
                        total = 0;
                        for (int i = 0; i < 100; ++i) {
                            pool.enqueue([this]() {
                                int value = ++total;
                                std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                                emit updateValue(value);
                                });
                        }
                    }
                    std::atomic<int> total = 0;
                    ThreadPool pool;
                Q_SIGNALS:
                    void updateValue(int);
                };
                

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

                A 1 Reply Last reply 20 days ago
                0
                • C Christian Ehrlicher
                  20 days ago

                  @aiphae said in Update UI from multiple threads:

                  void MainWindow::start() {
                      ThreadPool pool(std::thread::hardware_concurrency() - 1);
                      ....
                  }
                  
                  ~ThreadPool() {
                      {
                          std::unique_lock<std::mutex> lock(queueMutex);
                          stop = true;
                      }
                      condition.notify_all();
                      for (auto &worker : workers) {
                          worker.join();
                      }
                  }
                  
                  

                  What do you expect? You block the main event loop in the dtor.

                  /edit:
                  My Widget I used so it's a fully functional example which does not block:

                  class MyWidget : public QLabel
                  {
                      Q_OBJECT
                  public:
                      MyWidget()
                          : pool(std::thread::hardware_concurrency() - 1)
                      {
                          connect(this, &MyWidget::updateValue, this, [this](int value) {
                              setText(QString::number(value));
                              });
                      }
                      void start() {
                          total = 0;
                          for (int i = 0; i < 100; ++i) {
                              pool.enqueue([this]() {
                                  int value = ++total;
                                  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                                  emit updateValue(value);
                                  });
                          }
                      }
                      std::atomic<int> total = 0;
                      ThreadPool pool;
                  Q_SIGNALS:
                      void updateValue(int);
                  };
                  
                  A Offline
                  A Offline
                  aiphae
                  wrote 20 days ago last edited by aiphae
                  #12

                  @Christian-Ehrlicher Okay, I moved ThreadPool to the private members of StackPage and now I connect the signal this way:

                  connect(this, &StackPage::sortingProgressUpdated, this, [this](int current) {
                          qDebug() << current;
                          std::this_thread::sleep_for(std::chrono::milliseconds(100));
                          ui->analyzingProgressEdit->setText(QString::number(current) + "/" + QString::number(totalFrames));
                      }, Qt::QueuedConnection);
                  

                  qDebug() prints concurrently as expected, but the ui->analyzingProgressEdit doesn't get updated. Why?

                  UPD:
                  I added ui->analyzingProgressEdit->repaint() right after changing its value. It looks like all the slots connected to the signal are queued and executed only after the threads complete their tasks because the UI doesn't change at all at the beginning of the function (actual concurrent processing) and at the end updates very rapidly (processing queued signals).

                  I changed the number of threads for the thread pool to just 2 (though it could take all available threads?) - nothing changed.

                  UPD2:
                  Current code:

                  #include "mainwindow.h"
                  #include "ui_mainwindow.h"
                  #include <QThread>
                  
                  MainWindow::MainWindow(QWidget *parent)
                      : QMainWindow(parent)
                      , ui(new Ui::MainWindow)
                      , pool(std::thread::hardware_concurrency() - 1)
                  {
                      ui->setupUi(this);
                      connect(this, &MainWindow::update, this, [](int value) {
                          std::this_thread::sleep_for(std::chrono::milliseconds(100));
                          qDebug() << value << "from thread" << QThread::currentThread();
                      });
                      start();
                  }
                  
                  MainWindow::~MainWindow()
                  {
                      delete ui;
                  }
                  
                  void MainWindow::start() {
                      total = 0;
                      for (int i = 0; i < 100; ++i) {
                          pool.enqueue([this]() {
                              int value = ++total;
                              qDebug() << "Emitting signal with value" << value;
                              emit update(value);
                          });
                      }
                  }
                  
                  

                  Application output shows this:

                  Emitting signal with value 80
                  Emitting signal with value 90
                  Emitting signal with value 91
                  2 from thread QThread(0x2140d6406f0, name = "Qt mainThread")
                  4 from thread QThread(0x2140d6406f0, name = "Qt mainThread")
                  12 from thread QThread(0x2140d6406f0, name = "Qt mainThread")
                  ...
                  

                  so only after all signals from corresponding values are emitted (up to several hundreds or thousands depending on the data), their slots are executed.

                  1 Reply Last reply
                  0
                  • C Offline
                    C Offline
                    Christian Ehrlicher
                    Lifetime Qt Champion
                    wrote 20 days ago last edited by Christian Ehrlicher
                    #13

                    I don't know what you are doing wrong but my code works and even updates the ui. So either provide a minimal compilable example or fix your code...

                    #include <queue>
                    class ThreadPool {
                    public:
                        explicit ThreadPool(int threadNumber) {
                            for (int i = 0; i < threadNumber; ++i) {
                                workers.emplace_back([this] {
                                    for (;;) {
                                        std::function<void()> task;
                                        {
                                            std::unique_lock<std::mutex> lock(queueMutex);
                                            condition.wait(lock, [this] { return stop || !tasks.empty(); });
                                            if (stop && tasks.empty())
                                                return;
                                            task = std::move(tasks.front());
                                            tasks.pop();
                                        }
                                        task();
                                    }
                                    });
                            }
                        }
                        ~ThreadPool() {
                            {
                                std::unique_lock<std::mutex> lock(queueMutex);
                                stop = true;
                            }
                            condition.notify_all();
                            for (auto& worker : workers)
                                worker.join();
                        }
                    
                        template<typename F, typename... Args>
                        auto enqueue(F&& f, Args&&... args)
                            -> std::future<typename std::invoke_result<F, Args...>::type> {
                            using return_type = typename std::invoke_result<F, Args...>::type;
                    
                            auto task = std::make_shared<std::packaged_task<return_type()>>(
                                std::bind(std::forward<F>(f), std::forward<Args>(args)...)
                            );
                    
                            std::future<return_type> res = task->get_future();
                            {
                                std::unique_lock<std::mutex> lock(queueMutex);
                                if (stop)
                                    throw std::runtime_error("enqueue on stopped ThreadPool");
                                tasks.emplace([task]() { (*task)(); });
                            }
                            condition.notify_one();
                            return res;
                        }
                    private:
                        std::vector<std::thread> workers;
                        std::queue<std::function<void()>> tasks;
                    
                        std::mutex queueMutex;
                        std::condition_variable condition;
                        bool stop = false;
                    };
                    
                    class MyWidget : public QLabel
                    {
                        Q_OBJECT
                    public:
                        MyWidget()
                            : pool(std::thread::hardware_concurrency() - 1)
                        {
                            connect(this, &MyWidget::updateValue, this, [this](int value) {
                                setText(QString::number(value));
                                qDebug() << QThread::currentThread() << value;
                            });
                        }
                        void start() {
                            for (int i = 0; i < 100; ++i) {
                                pool.enqueue([this]() {
                                    int value = ++total;
                                    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                                    emit updateValue(value);
                                    });
                            }
                        }
                        std::atomic<int> total = 0;
                        ThreadPool pool;
                    Q_SIGNALS:
                        void updateValue(int);
                    };
                    
                    int main(int argc, char* argv[])
                    {
                        QApplication a(argc, argv);
                        MyWidget w;
                        w.show();
                        qDebug() << QThread::currentThread();
                        QTimer::singleShot(0, &w, &MyWidget::start);
                        return a.exec();
                    }
                    #include <main.moc>
                    

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

                    A 1 Reply Last reply 20 days ago
                    0
                    • C Christian Ehrlicher
                      20 days ago

                      I don't know what you are doing wrong but my code works and even updates the ui. So either provide a minimal compilable example or fix your code...

                      #include <queue>
                      class ThreadPool {
                      public:
                          explicit ThreadPool(int threadNumber) {
                              for (int i = 0; i < threadNumber; ++i) {
                                  workers.emplace_back([this] {
                                      for (;;) {
                                          std::function<void()> task;
                                          {
                                              std::unique_lock<std::mutex> lock(queueMutex);
                                              condition.wait(lock, [this] { return stop || !tasks.empty(); });
                                              if (stop && tasks.empty())
                                                  return;
                                              task = std::move(tasks.front());
                                              tasks.pop();
                                          }
                                          task();
                                      }
                                      });
                              }
                          }
                          ~ThreadPool() {
                              {
                                  std::unique_lock<std::mutex> lock(queueMutex);
                                  stop = true;
                              }
                              condition.notify_all();
                              for (auto& worker : workers)
                                  worker.join();
                          }
                      
                          template<typename F, typename... Args>
                          auto enqueue(F&& f, Args&&... args)
                              -> std::future<typename std::invoke_result<F, Args...>::type> {
                              using return_type = typename std::invoke_result<F, Args...>::type;
                      
                              auto task = std::make_shared<std::packaged_task<return_type()>>(
                                  std::bind(std::forward<F>(f), std::forward<Args>(args)...)
                              );
                      
                              std::future<return_type> res = task->get_future();
                              {
                                  std::unique_lock<std::mutex> lock(queueMutex);
                                  if (stop)
                                      throw std::runtime_error("enqueue on stopped ThreadPool");
                                  tasks.emplace([task]() { (*task)(); });
                              }
                              condition.notify_one();
                              return res;
                          }
                      private:
                          std::vector<std::thread> workers;
                          std::queue<std::function<void()>> tasks;
                      
                          std::mutex queueMutex;
                          std::condition_variable condition;
                          bool stop = false;
                      };
                      
                      class MyWidget : public QLabel
                      {
                          Q_OBJECT
                      public:
                          MyWidget()
                              : pool(std::thread::hardware_concurrency() - 1)
                          {
                              connect(this, &MyWidget::updateValue, this, [this](int value) {
                                  setText(QString::number(value));
                                  qDebug() << QThread::currentThread() << value;
                              });
                          }
                          void start() {
                              for (int i = 0; i < 100; ++i) {
                                  pool.enqueue([this]() {
                                      int value = ++total;
                                      std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                                      emit updateValue(value);
                                      });
                              }
                          }
                          std::atomic<int> total = 0;
                          ThreadPool pool;
                      Q_SIGNALS:
                          void updateValue(int);
                      };
                      
                      int main(int argc, char* argv[])
                      {
                          QApplication a(argc, argv);
                          MyWidget w;
                          w.show();
                          qDebug() << QThread::currentThread();
                          QTimer::singleShot(0, &w, &MyWidget::start);
                          return a.exec();
                      }
                      #include <main.moc>
                      
                      A Offline
                      A Offline
                      aiphae
                      wrote 20 days ago last edited by aiphae
                      #14

                      @Christian-Ehrlicher

                      Okay, I finally saw the issue. In my code std::this_thread::sleep_for(std::chrono::milliseconds(100)); was outside the pool.enqueue() function. So the main thread was constantly sleeping and couldn't process the signals.

                      1 Reply Last reply
                      2
                      • A aiphae has marked this topic as solved 20 days ago

                      14/14

                      25 Apr 2025, 11:45

                      • Login

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