LINUX.ORG.RU

Являются ли сигналы и слоты потокобезопасными?

 ,


0

3

Мое приложение должно выполнять вычисления в фоновом потоке. OK, использовал пример из документации Qt, сделал так:


    Worker *worker = new Worker(nullptr, 0.0, 100, 0.1);
    worker->moveToThread(&background);
    connect(&background, &QThread::finished, worker, &QObject::deleteLater);
    connect(this, &MainWindow::runCalculation, worker, &Worker::run);
    connect(worker, &Worker::calculated, this, &MainWindow::plotComputedData);
    connect(worker, &Worker::reportProgress,
          [this] (int x) {
         bar->setValue(x);});

    background.start(QThread::HighPriority);
    emit runCalculation();

//bar - QProgressBar

Теперь. Если я во время вычислений делаю emit reportProgress:

void Worker::run()
{
    if (_data.size() > 0)
        _data.clear();
    int i = 0;
    for (double x = _min; x < _max; x += _step, ++i)
    {
        _data.push_back(QPointF(x, getY(x)));
        //emit reportProgress(i); //ЗДЕСЬ
        QThread::msleep(10);
     }
    emit calculated(_data);
}

происходит сегфолт. emit calculated проходит нормально.

minimal working example: https://www.dropbox.com/s/thgxwc98fevi6v3/qwt-test2.tar.bz2?dl=0

★★★★

Пример не смотрел.

//emit reportProgress(i); //ЗДЕСЬ

с каким слотом соединено? Что в слоте?

UVV ★★★★★
()

Дык у тебя же лямбда выполняется в контексте потока, а изменяет bar, который в главном потоке.

panter_dsd ★★★★
()

Замени лямбду на коннект напрямую на QProgressBar::setValue и будет тебе счастье.

Да, когда выкладываешь пример - удостоверься, что он собирается.

panter_dsd ★★★★
()
Последнее исправление: panter_dsd (всего исправлений: 1)
Ответ на: комментарий от panter_dsd

ОП не читал

Тут коннекты нормально разрулятся и в автоконнекте.

Qt5? А прямой коннект (который компайл-тайм) не будет выполняться в контексте вызывающего потока?

Stil ★★★★★
()
Ответ на: комментарий от panter_dsd

не, там у меня просто еще одна штука была в этой лямбде, я ее убрал

pashazz ★★★★
() автор топика
Ответ на: комментарий от pashazz

Если заменишь лямбду на слот/метод QMainWindow, то заработает нормально.

panter_dsd ★★★★
()

Накиньтесь на меня и поправьте если ошибаюсь:

signal можно слать между потоками и это всегда безопасно ибо если что, то слоты вызываются как queued connection, даже если сначала мы сконнектились с объектом в одном потоке, а затем перенесли объект из которого или к которому летят сигналы - в другой поток.

А вот если слоты - если вызывать напрямую то они вызовутся в текущем потоке что небезопасно и выход из ситуации - слать сигнал туда либо дергать invoke method с указанием queued connection.

Кто понял кашу в моей голове, поправьте.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от pashazz

Так лямбда же копируется и Кьют не может разрулить в каком она потоке, поэтому юзает DirectConnection.

panter_dsd ★★★★
()
Ответ на: комментарий от pashazz

Кстати, можешь еще в лямбде обратиться к прогрессбару через инвокМетод.

panter_dsd ★★★★
()
Ответ на: комментарий от I-Love-Microsoft

signal можно слать между потоками и это всегда безопасно ибо если что, то слоты вызываются как queued connection, даже если сначала мы сконнектились с объектом в одном потоке, а затем перенесли объект из которого или к которому летят сигналы - в другой поток.

Если коннект был авто или очередью – да.

А вот если слоты - если вызывать напрямую то они вызовутся в текущем потоке что небезопасно и выход из ситуации - слать сигнал туда либо дергать invoke method с указанием queued connection.

Все верно.

Stil ★★★★★
()
Ответ на: комментарий от Stil

к сожалению, проблема не решена. Сегфолт.

0	QString::replace(QChar const*, int, QChar const*, int, Qt::CaseSensitivity)	QString::replace(QChar const*, int, QChar const*, int, Qt::CaseSensitivity)		0x100ec75ac	
1	QString::replace(QLatin1String, QString const&, Qt::CaseSensitivity)	QString::replace(QLatin1String, QString const&, Qt::CaseSensitivity)		0x100ec7f06	
2	QProgressBar::text() const	QProgressBar::text() const		0x100342ae2	
3	QProgressBar::initStyleOption(QStyleOptionProgressBar*) const	QProgressBar::initStyleOption(QStyleOptionProgressBar*) const		0x100341f9d	
4	QProgressBarPrivate::repaintRequired() const	QProgressBarPrivate::repaintRequired() const		0x10034210e	
5	QProgressBar::setValue(int)	QProgressBar::setValue(int)		0x100342676	
6	MainWindow::trackProgress(int)	mainwindow.cpp	128	0x10000709f	
7	QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<int>, void, void (MainWindow::*)(int)>::call(void (MainWindow::*)(int), MainWindow*, void**)	qobjectdefs_impl.h	500	0x100008d38	
8	void QtPrivate::FunctionPointer<void (MainWindow::*)(int)>::call<QtPrivate::List<int>, void>(void (MainWindow::*)(int), MainWindow*, void**)	qobjectdefs_impl.h	519	0x100008c90	
9	QtPrivate::QSlotObject<void (MainWindow::*)(int), QtPrivate::List<int>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)	qobject_impl.h	143	0x100008ac8	
10	QObject::event(QEvent*)	QObject::event(QEvent*)		0x10106d5c3	
11	QWidget::event(QEvent*)	QWidget::event(QEvent*)		0x10020aacd	
12	QMainWindow::event(QEvent*)	QMainWindow::event(QEvent*)		0x10030d191	
13	QApplicationPrivate::notify_helper(QObject*, QEvent*)	QApplicationPrivate::notify_helper(QObject*, QEvent*)		0x1001cadbb	
14	QApplication::notify(QObject*, QEvent*)	QApplication::notify(QObject*, QEvent*)		0x1001ce110	
15	QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*)	QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*)		0x101042c8b	
16	QCocoaEventDispatcherPrivate::processPostedEvents()	QCocoaEventDispatcherPrivate::processPostedEvents()		0x10342d1be	
17	QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)	QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)		0x10342c6ff	
18	MainWindow::trackProgress(int)	mainwindow.cpp	129	0x1000070be	
19	QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<int>, void, void (MainWindow::*)(int)>::call(void (MainWindow::*)(int), MainWindow*, void**)	qobjectdefs_impl.h	500	0x100008d38	

сделал так:

    connect(worker, &Worker::reportProgress, this, &MainWindow::trackProgress, Qt::QueuedConnection);

void MainWindow::trackProgress(int x)
{

    bar->setValue(x);
 
   qApp->processEvents();
}

Без последней строчки все работает, но GUI не отвечает.
pashazz ★★★★
() автор топика
Последнее исправление: pashazz (всего исправлений: 1)
Ответ на: комментарий от panter_dsd

да, уменьшил частоту, стало норм

pashazz ★★★★
() автор топика
Ответ на: комментарий от pashazz

Ненене.

Ты уверен что поток УЖЕ стартовал в момент сигнала? Попробуй выбрасывать сигнал или после QThread::started() или подождать QThread::isRunning() == true.

grondek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.