Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt GUI doesn't work with std::thread as I expect

The core of my project is independent of GUI framework that's why I prefer std::thread. But Qt gives me an error when thread is using.

The inferior stopped because it received a signal from the operating system.

Signal name: SIGSEGV
Signal meaning: Segmentation fault

//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <thread>
#include <mutex>
#include <QMainWindow>

namespace Ui { class MainWindow; }

struct Observer
{
    virtual void notify() = 0;
};

class Core
{
public:
    std::thread *run()
        {
            std::thread thread(&Core::runP, this);
            thread.detach();
            return &thread;
        }

    void setObserver(Observer *observer) { _observer = observer; }
    int ii() const { return _ii; }
    void nextIi() { _ii++; }

    void lock()    { _mutex.lock(); }
    bool tryLock() { return _mutex.try_lock(); }
    void unlock()  { _mutex.unlock(); }

private:
    void runP()
        {
            for (int i = 1; i <= 1000; i++) {
                if (i % 10 == 0) {
                    lock();
                    nextIi();
                    unlock();
                    notify();
                }
            }
        }

    void notify() { _observer->notify(); }  //!!!
    Observer *_observer;
    int _ii;
    std::mutex _mutex;
};

struct MwObserver : public Observer
{
    explicit MwObserver(struct MainWindow *mainWindow) { _mainWindow = mainWindow; }
    virtual void notify();

    MainWindow *_mainWindow;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow() { delete _ui; }
    void upd();

public slots:
    void run() { _core.run(); }

private:
    Ui::MainWindow *_ui;
    MwObserver _observer;
    Core _core;
};

inline void MwObserver::notify() { _mainWindow->upd(); }

#endif

-

//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    _ui(new Ui::MainWindow),
    _observer(this)
{
    _ui->setupUi(this);
    connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
}

void MainWindow::upd()
{
    _core.lock();
    setWindowTitle(QString::number(_core.ii()));
    _core.unlock();
}
like image 968
Ufx Avatar asked Apr 21 '16 19:04

Ufx


2 Answers

There are multiple problems here, first and most obvious was already noted by perencia. You are returning a pointer to stack variable. In c++ terms it's unacceptable.

Secondly. The crash comes from not using std::thread, but from race condition. The Qt event loop does not know about you mutex, so your setWindowTitle call is introducing a race, that leads to crash. You need to use QMetaObject::invokeMethod to post function to the Qts event loop.

Example: change

inline void MwObserver::notify() { _mainWindow->upd(); } 

to

inline void MwObserver::notify() {
    if(!QMetaObject::invokeMethod(_mainWindow, "upd", Qt::QueuedConnection))
        std::cerr << " Failed to invoke method" << std::endl;
} 

additional includes may apply

like image 82
UldisK Avatar answered Sep 27 '22 19:09

UldisK


This updates the GUI from a thread different then the GUI thread! Which is not allowed. Why not to use QThread and a signal/slot mechanism to update your window title. The Qt framework does the thread switching automatically.

class Core : public QObject 
{
  Q_OBJECT
public:
  explicit Core(QObject * parent = 0) : QObject(parent) {}

signals:
  void notify();

public slots:
  void nextIi() { _ii++; }
  void runP()
  {
    for (int i = 1; i <= 1000; i++) {
      if (i % 10 == 0) {
        nextIi();
        notify();
      }
    }
  }

private:
  Q_DISABLE_COPY(Core);
  int _ii;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void run() {_th.start();}
    void upd(int ii) {setWindowTitle(QString::number(ii));}

private:
    Ui::MainWindow *_ui;
    Core _core;
    QThread _th;
};

//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    _ui(new Ui::MainWindow),
    _observer(this)
{
  _ui->setupUi(this);

  connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
  connect(&_core, SIGNAL(notify(int)), this, SLOT(upd(int)));
  _core.moveToThread(&_th);
}

MainWindow::~MainWindow()
{ 
  delete _ui; 
  _th.quit();
  _th.wait(1000);
}
like image 42
jonjonas68 Avatar answered Sep 27 '22 17:09

jonjonas68