Russian Qt Forum

Qt => Установка, сборка, отладка, тестирование => Тема начата: AlekseyK от Октябрь 27, 2009, 19:24



Название: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Октябрь 27, 2009, 19:24
Вобщем имеем такой код:
Код:
    QEventLoop loop;
    QFutureWatcher<void> watcher;
    connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
    QFuture<void> future = QtConcurrent::run(this, &MyClass::MyMethod, parameters...);
    watcher.setFuture(future);
    loop.exec();

При использовании такой конструкции вылетает segmentation fault. Если просто вызвать MyMethod с параметрами, то всё работает без ошибок, но это неудобно т.к. в этом методе запускается продолжительный вычислительный процесс и хочется, чтобы он выполнялся в отдельном потоке. Если зкомментировать цикл и QFutureWatcher, то вылетает уже другая ошибка, но суть та же. Воспроизводится как в Qt 4.5.3, так и в 4.6.0-beta1, и в Windows, и в Linux.

В чём может быть проблема: это моя ошибка или баг Qt и надо им писать отчёт?

Думаю ещё запустить Valgrind, чтобы проверить нет ли где ошибки обращения к памяти в моём приложении.

Цитата: Вывод приложения:
ASSERT: "qApp && qApp->thread() == QThread::currentThread()" in file kernel\qapplication_win.cpp, line 895
QWaitCondition: Destroyed while threads are still waiting


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Rcus от Октябрь 27, 2009, 19:46
Ну вот именно это утверждение должно гарантировать что к функциям рисования будут обращаться только из главного потока, что происходит в другой системе тоже надо смотреть, а вообще так ли сложно запустить отладчик и узнать след стека вызовов?
Я тоже не любил отладчики, но после сеанса ловли SIGSEGV при инициализации фреймбуффера без отладочных символов (памяти не хватало) очень даже полюбил их (хотя без крайней необходимости использую только для посмертного изучения процесса)


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Октябрь 27, 2009, 20:36
Ну вот именно это утверждение должно гарантировать что к функциям рисования будут обращаться только из главного потока
Понятно, этот момент я видимо пропустил в документации. Благодарю! Исправил. Я использовал QTextEdit для вывода отладочной информации из этой функции и... repaint(),  ;D чтобы что-то увидеть пока идут вычисления, т.к. они выполнялись в том же потоке, теперь я благополучно убрал ui->errorEdit->repaint() за ненадобностью, всё работает! Спасибо.

P.S. Qt таки рулит! ;)
P.P.S. А чем всё-таки может быть связана такая особенность Qt Concurrent?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Октябрь 29, 2009, 14:47
В винде продолжает вылетать, стек ни о чём ни говорит, всегда запускаю в отладчике пока что:

Код
0 ntdll!LdrDestroyOutOfProcessImage C:\WINDOWS\system32\ntdll.dll 0 0x7c8285f3
1 ntdll!LdrAddRefDll C:\WINDOWS\system32\ntdll.dll 0 0x7c82860c
2 ntdll!ZwClose C:\WINDOWS\system32\ntdll.dll 0 0x7c826d49
3 ?? C:\WINDOWS\system32\vfbasics.dll 0 0x00392f52
4 ?? 0 0x00000000
 

Цитата: Вывод приложения
ASSERT: "qApp && qApp->thread() == QThread::currentThread()" in file kernel\qapplication_win.cpp, line 895
QWaitCondition: Destroyed while threads are still waiting
QObject::killTimers: timers cannot be stopped from another thread

В чём может быть проблема?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 16:48
Я нашёл: в моём методе оставался вызов:

ui->errorEdit->append(s);

Линукс с ним нормально работает, windows - вываливает ошибку. Я смотрю, что QtConcurrent может передавать информацию во вне только через progressText() и progressValue(). А как можно менять их значение в MyMethod?!


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 15, 2010, 16:56
Линукс с ним нормально работает, windows - вываливает ошибку. Я смотрю, что QtConcurrent может передавать информацию во вне только через progressText() и progressValue(). А как можно менять их значение в MyMethod?!

Это особенность потоков. ОС ни при чем. Унаследуй свой класс от QObject'a и отсылай сигналы. progressValue() меняется автоматически, если ты используешь контейнеры/списки/листы в методе типа QtConcurrent::map(). Для QtConcurrent::run() он не сигналит:

Цитировать
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 17:23
Это особенность потоков. ОС ни при чем. Унаследуй свой класс от QObject'a и отсылай сигналы.
А он так и унаследован - от QMainWindow, а MyMethod определён в нём, и в этом же классе в другом методе создаётся QFuture объект.

progressValue() меняется автоматически, если ты используешь контейнеры/списки/листы в методе типа QtConcurrent::map(). Для QtConcurrent::run() он не сигналит:
Понятно, будем разбираться с map: с run() всё так просто - запускаешь свою функцию... ЛЮБУЮ, и порядок! ;) Но что интересно в линуксе:
Код:
ui->errorEdit->append(s);
и
progressBar->setValue(i * 100 / cycles);
работают внутри MyMethod(), хотя похоже, что ошибки иногда всё-таки вылетают, а в винде отказывается категорически.

Цитировать
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
Понятно, спасибо, не дочитал просто до этого места.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 17:35
Блин, это ж для map придётся логику MyMethod() полностью менять?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 17:56
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.

Я смотрю, что ты в своё время тоже на это налател:
http://www.qtcentre.org/threads/26861-QtConcurrent-i-need-advice? ;)

А что означала твоя фраза:
Код:
I solved my task himself, with signals and slots and using QtConcurrent::run(). 
?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 19:58
Передалал код так:
Код:
    QEventLoop loop;
    QFutureWatcher<QString> watcher;
    connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
    // Prepare cycles vector
    QVector<int> vector;
    for (int i = 0; i < cyclesCount; i++)
        vector.append(i);
    // Run thread
    watcher.setFuture(QtConcurrent::mapped(vector,  &MyClass::MyMethod));

не собирается. Ошибка:

Цитировать
no matching function for call to 'mapped(QVector<int>&, QString (MyClass::*)(int&))'

Чего ему не хватает? Или он методы класса не поддерживает?

MyClass, напомню,-  производный от QMainWindow и всё в нём происходит.
Объявление MyMethod:
QString MyMethod(int &cycleNo);


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 20:15
Блин, похоже он это не поддерживает и надо будет использовать QThread?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 15, 2010, 22:07
#include <QtConcurrentMap>


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 15, 2010, 23:59
#include <QtConcurrentMap>

Благодарствую, добавил. Но ошибка пока всё та же:

Цитировать
no matching function for call to ‘mapped(QVector<int>&, QString (MyMainWindow::*)(const int&))’


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 11:06
Ау, люди???!!! Есть идеи?! Или может у кого есть пример работающего собирающегося аналогичного кода, пожалуйста?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 16, 2010, 12:41
Пример


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 13:34
Пример

В Вашем примере функция статическая - в моём случае неприменима, т.к. она требует кучу нестатических членов-переменных класса.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 16, 2010, 13:39
Подружить QtConcurrentMap с нестатическим методом у меня так и не получилось.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 13:43
Параметр в MyMethod объявил, а передать в mapped забыл.

Код
C++ (Qt)
QtConcurrent::mapped(vector,  &MyClass::MyMethod, arg)
 

Как это забыл?! Как его передать? Там нет подобного вызова mapped, есть 2 варианта:
Цитировать
QFuture<T>   mapped ( const Sequence & sequence, MapFunction function )
QFuture<T>   mapped ( ConstIterator begin, ConstIterator end, MapFunction function )

Использую первый.

В примерах из документации Qt, например progressdialog, в mapped больше ничего не передаётся:
Цитировать
void spin(int &iteration)
 {
    ...
}

 int main(int argc, char **argv)
 {
     ...
     // Prepare the vector.
     QVector<int> vector;
     for (int i = 0; i < iterations; ++i)
         vector.append(i);

     // Create a progress dialog.
....
     // Create a QFutureWatcher and conncect signals and slots.
     QFutureWatcher<void> futureWatcher;
....
     // Start the computation.
     futureWatcher.setFuture(QtConcurrent::map(vector, spin));
....
}


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 13:46
Подружить QtConcurrentMap с нестатическим методом у меня так и не получилось.

Вот то-то ж и оно! Придётся видимо использовать QThread. Думал, что Qt действительно что-то изобрело путное для многопоточности, ан нет - те же костыли и проблемы, что и при обыкновенном процедурном программировании: главная проблема - как передать кучу данных потоку и сделать это по возможности красиво ;)


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 16, 2010, 14:06
Попробуй вместо mapped использовать blockingMappedReduced. Судя по описанию, должно работать. Только медленно.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 14:35
Попробуй вместо mapped использовать blockingMappedReduced. Судя по описанию, должно работать. Только медленно.
Попробую, а почему медленно?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 16, 2010, 14:57
Вот то-то ж и оно! Придётся видимо использовать QThread. Думал, что Qt действительно что-то изобрело путное для многопоточности, ан нет - те же костыли и проблемы, что и при обыкновенном процедурном программировании: главная проблема - как передать кучу данных потоку и сделать это по возможности красиво ;)

Используй boost::bind, std::tr1::bind или std::bind1st + std::mem_fun

Код
C++ (Qt)
#include <QtCore/QtConcurrentMap>
#include <functional>
 
QString MyClass::myMethod(int arg)
{
...
}
...
QtConcurrent::mapped(vector, std::bind1st(std::mem_fun(&MyClass::myMethod), this));
 


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 16, 2010, 15:09
Попробую, а почему медленно?

Потому что blocking :)


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 15:55
Вот то-то ж и оно! Придётся видимо использовать QThread. Думал, что Qt действительно что-то изобрело путное для многопоточности, ан нет - те же костыли и проблемы, что и при обыкновенном процедурном программировании: главная проблема - как передать кучу данных потоку и сделать это по возможности красиво ;)

Используй boost::bind или std::tr1::bind.

В смысле? Для чего использовать? Для потоков или для mapped?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 16, 2010, 16:01
Для mapped.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 16:39
Код
C++ (Qt)
QtConcurrent::mapped(vector, std::bind1st(std::mem_fun(&MyClass::myMethod), this));

Не работает:

Цитировать
c:\Qt\2010.02.1\qt\include/QtCore/../../src/corelib/concurrent/qtconcurrentmapkernel.h:180: error: no match for call to '(std::binder1st<std::mem_fun1_t<QString, MyMainWindow, int&> >) (const int&)'


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 16, 2010, 18:04
У меня всё работает.

Код
C++ (Qt)
#include <QtConcurrentMap>
#include <functional>
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
   QMainWindow(parent),
   ui(new Ui::MainWindow)
{
   ui->setupUi(this);
   QVector<int> test;
   test << 1 << 2 << 3;
 
   QtConcurrent::mapped(test, std::bind1st(std::mem_fun(&MainWindow::myMethod), this));
}
 
int MainWindow::myMethod(int i)
{
   qDebug() << i;
   return i;
}
 

Код:
2 
1
3

Вариант с tr1:

Код
C++ (Qt)
#include <QtConcurrentMap>
#include <tr1/functional>
 
...
QtConcurrent::mapped(test, std::tr1::bind(&MainWindow::myMethod, this, std::tr1::placeholders::_1));
 


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 19:01
Спасибо, у меня теперь тоже: заменил указатель на аргумент (как было в примере QtConcurrent) его значением, было так:

Код
C++ (Qt)
int MainWindow::myMethod(int &i)

Собралось. Но теперь myMethod валится на ровном месте. Или такой вызов некорректен, или следующий экземпляр myMethod вызывается до того, как отработал предыдущий и возникает конфликт, такое может быть?


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 16, 2010, 19:06
вызывается до того, как отработал предыдущий и возникает конфликт, такое может быть?

Вообще весь смысл потоков как раз в этом, чтобы методы вызывались и отрабатывались параллельно, без ожидания окончания параллельных потоков. Если у тебя где-то в коде есть подобный конфликт, то нужно переписать, чтобы его не было или пройтись дебагером, чтобы выяснить причину, если одновременного доступа к одному и тому же объекту не предполагается и не наблюдается.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 19:13
вызывается до того, как отработал предыдущий и возникает конфликт, такое может быть?

Вообще весь смысл потоков как раз в этом, чтобы методы вызывались и отрабатывались параллельно, без ожидания окончания параллельных потоков. Если у тебя где-то в коде есть подобный конфликт, то лучше пройтись дебагером.

Блин, опять:
Цитировать
The QtConcurrent::map(), QtConcurrent::mapped() and QtConcurrent::mappedReduced() functions run computations in parallel on the items in a sequence such as a QList or a QVector.

Мне нужно, чтобы вычисления выполнялись параллельно потоку главного окна, НО ПОСЛЕДОВАТЕЛЬНО, чтобы оно продолжало реагировать и в него в реальном времени выводился бы лог. Если следующий поток MyMethod запускается до того, как отработал предыдущий, то это не годится т.к. они меняют в цикле один и тот же массив информации последовательно - итерациями, параллельно их выполнять нельзя.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: Kolobok от Апрель 16, 2010, 20:25
А чем тогда QtConcurrentRun не устраивает.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 20:39
Будем использовать QThread, но всё равно хороший тред получился: может кому пригодится.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: SABROG от Апрель 16, 2010, 21:23
Попробуй вызвать это перед запуском потока:

Код
C++ (Qt)
QThreadPool::globalInstance()->setMaxThreadCount(0);
 

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

Но как уже правильно сказали, если контейнер невозможно распараллелить, то возвращаемся к QtConcurrent::run(), он запускает только один экземпляр потока на одном ядре. Но и QThread в этом случае тоже хороший вариант, так как предоставляет сразу нужный каркас для работы с сигналами и слотами, в то время как QtConcurrent::run() изначально работает с статическими функциями, но также легко работает с методом любого класса, в том числе на базе QObject'a.


Название: Re: Аварийное завершение программы при использованиии Concurrent API
Отправлено: AlekseyK от Апрель 16, 2010, 23:13
Но и QThread в этом случае тоже хороший вариант, так как предоставляет сразу нужный каркас для работы с сигналами и слотами, в то время как QtConcurrent::run() изначально работает с статическими функциями, но также легко работает с методом любого класса, в том числе на базе QObject'a.

Вот на нём и остановлюсь! ;) Каркас уже сделал. Благодарю всех за ваше время!