簡體   English   中英

Qt GUI不能像我期望的那樣使用std :: thread

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

我的項目的核心是獨立於GUI框架,這就是為什么我更喜歡std :: thread。 但是當線程使用時Qt給我一個錯誤。

劣勢停止是因為它收到了來自操作系統的信號。

信號名稱:SIGSEGV
信號含義:分段故障

//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();
}

這里存在多個問題,首先是perencia已經注意到的最明顯的問題。 您正在返回指向堆棧變量的指針。 用c ++術語來說,這是不可接受的。

其次。 崩潰來自於不使用std::thread ,而是來自競爭條件。 Qt事件循環不知道你的互斥,所以你的setWindowTitle調用引入了一場比賽,導致崩潰。 您需要使用QMetaObject :: invokeMethod將函數發布到Qts事件循環。

示例:更改

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

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

額外的包括可能適用

這將從不同於GUI線程的線程更新GUI! 這是不允許的。 為什么不使用QThread和信號/插槽機制來更新窗口標題。 Qt框架自動執行線程切換。

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);
}

您正在堆棧上創建線程並返回指向該線程的指針。 run() ,指針不再有效。

除了返回指向堆棧變量的指針以及從QT未知的線程對象更新GUI。 我沒有從你的代碼中看到你在哪里設置了Core類的_observer成員。 MainWindow類的_core成員沒有setObserver調用。

因此, MainWindow類的_core調用_core成員的_core ,但之后_core._observer包含垃圾。 我認為這是在調用Core類的notify方法時出現Segmentaion Fault的原因。

我已經給出了所有問題的答案,讓我總結一下。

程序崩潰無關和穿線,問題是, _observer_core成員MainWindow沒有設置。 必須添加對setObserver的調用。

explicit MainWindow( QWidget *parent = nullptr ) :
    QMainWindow( parent ),
    _observer( this )
{
    _core.setObserver( &_observer );
}

這將導致下一個問題,即觀察者實際上從另一個線程調用udp消息,導致在不同的線程上下文中進行UI更新。 要解決這個問題,最簡單的方法是使用Qt的Qt::QueuedConnection 要啟用此功能,我們必須使upt()成為一個插槽。

public slots:

void run();
void upd();

然后我們可以使用QMetaObject::invokeMethod來調用它

inline void MwObserver::notify()
{
    QMetaObject::invokeMethod( _mainWindow, "upd", Qt::QueuedConnection );
}

或者通過從QObject導出MwObserver ,給它一個信號,並將該信號連接到upd槽並在notify提升信號來使用信號/槽連接。

struct MwObserver 
    : public QObject
    , public Observer
{
    Q_OBJECT;

    signals:
    void sigUpd();

public:
    explicit MwObserver( MainWindow *mainWindow );

    virtual void notify()

    MainWindow *_mainWindow;
};

void MwObserver::notify()
{
    sigUpd();
}

MwObserver::MwObserver( MainWindow *mainWindow )
{
    _mainWindow = mainWindow;
    connect( this, SIGNAL(sigUpd()), _mainWindow, SLOT(upd()) )
}

免責聲明:我有一段時間沒有使用過Qt,但是在Linux / UNIX上使用X / XMotif,GUI必須在“主線程”中運行,而不是在生成的線程中運行。 也許這適用於您的情況。 只是想一想,讓你的GUI代碼在主線程中運行。

最好的方法是使用QObejct實例包裝純C ++代碼,並在此對象從純C ++代碼接收一些通知時觸發信號。

在你的情況下:

class MwObserver : public QObject, public Observer
{
    Q_OBJECT
public:
    explicit MwObserver(QObject *parent)
      : QObject(parent)
    {}

signals:
    void SomeEvent();

protected:
    // Observer
    void notify() {
       emit SomeEvent();
    }
};

現在MainWindow應該以這種方式將一些插槽連接到信號,並且一切都應該開箱即用(Qt將在幕后跳線)。

您的代碼表單注釋中 ,崩潰是由無效使用臨時對象引起的。 這是INVALID C ++代碼無論返回什么樣的對象:

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

您無法返回指向函數方法的本地對象的指針,因為當您返回函數時此對象立即變為無效。 這是基本的C ++知識。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM