2016-04-21 22 views
6

Il nucleo del mio progetto è indipendente dal framework GUI, ecco perché preferisco std :: thread. Ma Qt mi dà un errore quando il thread sta usando.Qt GUI non funziona con std :: thread come previsto

L'inferiore si è fermato perché ha ricevuto un segnale dal sistema operativo.

Nome del segnale: SIGSEGV
segnale che significa: 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(); 
} 
+0

QThread è ed è stato multipiattaforma molto tempo prima che std :: thread venisse creato. – dtech

+0

@ddriver Ho modificato – Ufx

+7

Ummm non è stato creato in pila? – perencia

risposta

7

Si sta creando discussione sullo stack e restituire un puntatore a tale. Dopo run() quel puntatore non è più valido.

+0

' std :: thread * thread = new std :: thread (& Core :: runP, this); 'fornisce lo stesso effetto. – Ufx

9

Questo aggiorna la GUI da un thread diverso dal thread della GUI! Che non è permesso Perché non utilizzare QThread e un meccanismo segnale/slot per aggiornare il titolo della finestra. Il framework Qt cambia automaticamente il thread.

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

Se è possibile, non voglio rendere il core dipendente da Qt. – Ufx

+0

il tuo esempio non funziona per me. – Ufx

11

Qui ci sono diversi problemi, primo e più ovvio è stato già notato da Perencia. Stai restituendo un puntatore per impilare la variabile. In termini C++ è inaccettabile.

In secondo luogo. Il crash viene dal non utilizzare std::thread, ma dalle condizioni di gara. Il ciclo di eventi Qt non conosce il mutex, quindi la tua chiamata a setWindowTitle sta introducendo una gara, che porta al crash. È necessario utilizzare QMetaObject::invokeMethod per inviare la funzione al ciclo di eventi Qts.

Esempio: cambiamento

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

a

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

aggiuntivo include possono applicare

+0

potresti suggerirmi cosa devo fare esattamente? (std :: thread * thread = new std :: thread (& Core :: runP, this); dà lo stesso effetto.) – Ufx

+0

@ufx risolve solo il problema con la variabile stack. Aggiornerò la risposta con l'esempio invokeMethod – UldisK

+0

ma non mi ha aiutato. – Ufx

0

Diniego: Non ho usato Qt in un certo tempo, ma con X/XMotif su Linux/UNIX la GUI DEVE essere eseguita nei thread 'main-thread', non generati. Forse questo si applica alla tua situazione. Solo un pensiero, avere il codice GUI eseguito nel thread principale.

0

L'approccio migliore consiste nel racchiudere codice C++ puro con istanze QObejct e segnali di attivazione quando questo oggetto riceve una notifica dal puro codice C++.

Quindi nel tuo caso:

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

signals: 
    void SomeEvent(); 

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

Ora MainWindow dovrebbe collegare alcune slot di segnale fornito in questo modo e tutto dovrebbe funzionare out of the box (Qt farà filo salto dietro le quinte).

In your code form comment l'arresto anomalo è causato dall'utilizzo non valido dell'oggetto temporaneo.Questo è VALIDO C++ codice non mater che tipo di oggetto viene restituito:

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

non puoi tornare un puntatore ad oggetto locale del metodo della funzione, dal momento che questo oggetto non è più valido immediatamente quando si torna una funzione. Questa è una conoscenza base di C++.

+0

Non mi ha aiutato. C'è lo stesso errore. http://pastebin.com/GBjnK5Va – Ufx

+1

vedi aggiornamento. Inoltre ti consiglio di abbandonare l'uso dei thread. Sono relè difficili da imparare e mantenere. Gli errori hanno fatto indicare che si hanno problemi con le conoscenze di base sul C++ stesso.Quindi, prima impara le cose di base prima di usare i thread. –

+0

Posso passare a 'void'. Comunque non ho ancora usato il valore di ritorno di questa funzione. Cosa ho fatto di sbagliato qui? http://pastebin.com/GBjnK5Va – Ufx

2

A parte il ritorno del puntatore allo stack della variabile e all'aggiornamento della GUI dall'oggetto thread non noto per QT. Non vedo dal tuo codice, dove hai impostato il membro _observer della classe Core. Nessuna chiamata setObserver per _core membro della classe MainWindow.

Quindi, consigltor di MainWindow chiamate di classe membro di _core membro, ma dopo che _core._observer contiene immondizia. Penso che questa sia la causa del tuo Segmentaion Fault in chiamata del metodo notify della classe Core.

2

Le risposte a tutti i problemi sono già state date, permettetemi di riassumere.

Il blocco del programma non ha nulla a che fare con il threading, il problema è che il nel membro _core di MainWindow non è impostato. È necessario aggiungere una chiamata a setObserver.

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

Questo porterà al problema successivo, che l'osservatore chiama effettivamente il messaggio udp da un altro thread, causando un aggiornamento interfaccia utente in un contesto diverso thread. Per risolvere questo, è più facile usare Qt's Qt::QueuedConnection. Per abilitare questo dobbiamo fare uno upt() uno slot.

public slots: 

void run(); 
void upd(); 

Quindi possiamo o chiamare utilizzando QMetaObject::invokeMethod in

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

o utilizzare una connessione di segnale/fessura derivando MwObserver da QObject, dando un segnale, e collegare tale segnale alla fessura upd e alzando il segnale in 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())) 
}