我的项目核心独立于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();
}
答案 0 :(得分:11)
这里存在多个问题,首先是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;
}
其他包含可能适用
答案 1 :(得分:9)
这将从与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);
}
答案 2 :(得分:7)
您正在堆栈上创建线程并返回指向该线程的指针。在run()
之后指针不再有效。
答案 3 :(得分:2)
除了返回指向堆栈变量的指针以及从QT未知的线程对象更新GUI之外。我没有从您的代码中看到您在_observer
类的Core
成员设置的位置。对setObserver
类的_core
成员没有MainWindow
次来电。
因此MainWindow
类的consructor调用了_core
成员的consructor,但之后_core._observer
包含了垃圾。我认为这是Segmentaion Fault
调用notify
Core
类onSortIndicatorOrderChanged: {
model = null
MyModel.Sort(...)
model = MyModel
}
方法的原因。
答案 4 :(得分:2)
已经给出了所有问题的答案,让我总结一下。
程序崩溃与线程无关,问题是_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 );
}
或通过从MwObserver
派生QObject
,给它一个信号,并将该信号连接到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()) )
}
答案 5 :(得分:0)
免责声明:我还没有在一段时间内使用过Qt,但是在Linux / UNIX上使用X / XMotif,GUI必须在&#39;主线程中运行,而不是产生线程。也许这适用于您的情况。只是想一想,让你的GUI代码在主线程中运行。
答案 6 :(得分:0)
最好的方法是使用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会在幕后跳线)。
在your code form comment中,崩溃是由无效使用临时对象引起的。这是 INVALID C ++ 代码无论返回什么类型的对象:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
您无法返回指向函数方法的本地对象的指针,因为此对象在返回函数时会立即失效。这是基本的C ++知识。