Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Hero Sanya от Май 20, 2020, 13:26



Название: Пауза
Отправлено: Hero Sanya от Май 20, 2020, 13:26
Как ставить функцию, выполняющуюся в потоке, на паузу, при нажатии кнопки?
Я сделал кнопочку, которая собирает данные с полей для ввода и отправляет их в функцию обработки, из неё, в функцию расчёта, в другом потоке(функция очень большая и долго считающая). Мне нужно, чтобы можно было прерывать выполнение этой функции в любой момент (нажимать на паузу). Я даже не представляю, что должно происходить в коде, при нажатии на кнопку "пауза".


Название: Re: Пауза
Отправлено: Igors от Май 20, 2020, 14:34
Ну если "без затей" то просто sleep в цикле, напр
Код
C++ (Qt)
void CheckPause( void )
while (pauseFlag)
 QThread::sleep(20);
}
 
И повтыкать эту ф-цию в тело длинных расчетов. Ну а кнопочка устанавливает переменную pauseFlag. И, кстати, "прерывать" и "останавливать" не одно и то же


Название: Re: Пауза
Отправлено: Авварон от Май 20, 2020, 14:57
https://code.woboq.org/qt5/include/qt/QtCore/qfuture.h.html#_ZN7QFuture5pauseEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZNK20QFutureInterfaceBase8isPausedEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZN20QFutureInterfaceBase13waitForResumeEv


Название: Re: Пауза
Отправлено: Hero Sanya от Май 20, 2020, 16:10
Ну если "без затей" то просто sleep в цикле, напр
Код
C++ (Qt)
void CheckPause( void )
while (pauseFlag)
 QThread::sleep(20);
}
 
И по вставлять эту функцию в тело длинных расчетов. Кнопочка устанавливает переменную pauseFlag. И, кстати, "прерывать" и "останавливать" не одно и то же

Так, то есть, обернуть всю функцию с расчётами в кокон из while(pauseFlag)? На само деле я не знаю что такое pauseFlag. Я к тому, что, нужны ли там библиотеки? И я так подразумеваю, что в итоге пауза будет ставится не сразу по нажатию кнопки, а пока она сигнал не поймает?


Название: Re: Пауза
Отправлено: Hero Sanya от Май 20, 2020, 16:12
https://code.woboq.org/qt5/include/qt/QtCore/qfuture.h.html#_ZN7QFuture5pauseEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZNK20QFutureInterfaceBase8isPausedEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZN20QFutureInterfaceBase13waitForResumeEv
О май гад. Что это за гиганский h. файлы?


Название: Re: Пауза
Отправлено: Igors от Май 21, 2020, 10:47
Так, то есть, обернуть всю функцию с расчётами в кокон из while(pauseFlag)?
Наоборот, вставить эту ф-цию в длинные расчеты, напр в тело цикла
На само деле я не знаю что такое pauseFlag.
Обычная переменная (лучше член класса) которую Вам надо завести
Я к тому, что, нужны ли там библиотеки?
Нет
И я так подразумеваю, что в итоге пауза будет ставится не сразу по нажатию кнопки, а пока она сигнал не поймает?
Да, будет считать пока дело не дойдет до ф-ции

https://code.woboq.org/qt5/include/qt/QtCore/qfuture.h.html#_ZN7QFuture5pauseEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZNK20QFutureInterfaceBase8isPausedEv
https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfutureinterface.cpp.html#_ZN20QFutureInterfaceBase13waitForResumeEv
Танки вперед :) Я всегда считал футуру бездарной, ну может я не прав. Можно пример (пседокод, словами, как угодно) как ее задействовать в данном случае? Спасибо


Название: Re: Пауза
Отправлено: Пантер от Май 21, 2020, 11:16
Я бы не стал использовать циклы со слипом, ибо это костыльное решение. На счет QFuture ничего сказать не могу, но я не уверен, что там так все просто, твой код должен поддерживать приостановку (если не прав, поправьте, самому лень читать доки).
Я поступил бы по-другому - реализовал расчеты так, чтобы можно было либо безболезненно остановить их, сохранив промежуточные результаты. Если это никак не получается, то хотя бы сделать все по-нормальному, используя QMutex, QSemaphore, QWaitCondition.


Название: Re: Пауза
Отправлено: Igors от Май 21, 2020, 12:01
...ибо это костыльное решение.
Да. Зато "дешево, удобно, практично".

Я поступил бы по-другому - реализовал расчеты так, чтобы можно было либо безболезненно остановить их, сохранив промежуточные результаты. Если это никак не получается, ..
Такая возможность выпадает редко. Обычно расчет наструячит массу своих данных, объектов и.т.п. и сохранить/восстановить этот "контекст" оказывается неподъемным.

..то хотя бы сделать все по-нормальному, используя QMutex, QSemaphore, QWaitCondition.
Как только Вы попробуете привести пример "по-нормальному" - все окажется очень непросто :)


Название: Re: Пауза
Отправлено: Авварон от Май 21, 2020, 18:42
Я поступил бы по-другому - реализовал расчеты так, чтобы можно было либо безболезненно остановить их, сохранив промежуточные результаты. Если это никак не получается, то хотя бы сделать все по-нормальному, используя QMutex, QSemaphore, QWaitCondition.

И это ровно то что делает QFutureInterface=)
На самом деле надо попедалировать рассылку Qt на тему того чтобы переименовать его в QPromise и таки задокументировать (это уже всплывало ~месяц назад но как всегда заглохло)


Название: Re: Пауза
Отправлено: Hero Sanya от Май 21, 2020, 22:42
Так. В общем, я сделал три кнопки:Пауза, продолжить и старт. Старт запускает программу и она сидит в цикле(пока, не тру), если паузы так и не произошло, то просто выйдет из него поставив тру. По пути она будет считывать файл в котором будет лежать ключ(ну просто код), собственно этот код изменяется нажатием пауза. То есть пауза, просто берёт файл, переписывает его содержимое в единичку(можно сказать что это ключ для паузы). собственно, когда глобальная функция прочтёт из файла единицу, она войдёт в замкнутый цикл.На всякий случай я там оставил таймер. там тоже происходит чтение файла и если была нажата кнопка продолжить, файл пере пишется ну, на любую другую цифру(например 2). То есть файлом, можно игнорировать такую вещь, как потоки и передавать послания в любые другие потоки или основной. Можете назвать это костылём, но это работает идеально, так ещё и оставляет возможность для модификаций, например для сериализации кода(что пригодится для сохранения и загрузки).

В итоге: Пауза и продолжить, просто переписывают по нажатию кнопки текст в файле, а циклы являются скажем так, контрольными точками или чекпоинтами.


Название: Re: Пауза
Отправлено: Hero Sanya от Май 21, 2020, 23:01
Если кому нужно, я приложу своё решение проблемы с паузой. Однако, тут я всё ещё не знаю, как реализовать неактивные кнопки(НО я думаю, это тема другой..эмм. темы). Так же, я считаю. что тут единственная дыра, это когда пользователь нажмёт на кнопку во время чтения из файла. С другой стороны, для выхода из паузы, можно придумать один единственный код: какие нибудь 8888, да он пропустит одну итерацию в цикле, но в итоге всё ровно продолжит программу.
.h
Код:
#ifndef THREE_H
#define THREE_H
void start(bool stoper);
#endif // THREE_H
.Cpp
Код:
#include "mainwindow.h"
#include <math.h>

#include <iostream>
using namespace std;

#include <thread> //две библиотеки,
#include <chrono> //для потоков
#include "three.h"
#include <stdlib.h>

void one(bool stoper){
    int i=0;
    char str[10];
    int n=0;
    FILE* fan;


    while(stoper==false){
    cout<<"i="<<i<<endl; i++;
    this_thread::sleep_for(chrono::milliseconds(1500));

      fan=fopen("test.txt","r");
      while(!feof (fan)) {
      if (fgets(str, 10, fan))
       n = atoi(str);
 cout<<"s="<<str<<endl;
 this_thread::sleep_for(chrono::milliseconds(1000));
 cout<<"n="<<n<<endl;
 this_thread::sleep_for(chrono::milliseconds(1000));
    }

  this_thread::sleep_for(chrono::milliseconds(3000));
     if(n==1){
         while(n==1){
             cout<< "pause" << endl;
             this_thread::sleep_for(chrono::milliseconds(1000));
             rewind(fan);
             while(!feof (fan)) {
                 cout<< "@";
             if (fgets(str, 10, fan))
              n = atoi(str);
             cout<<"n in pause="<< n << "pause" << endl;
         }
         }
         cout<< "continue" << endl;
         this_thread::sleep_for(chrono::milliseconds(200));
         }
     //stoper=true;
    }
fclose(fan);
}



void start(bool stoper){
thread th(one,stoper);
th.detach();
}


MainWindow::MainWindow(QWidget *parent):
Код:
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    FILE* fn;
    fn=fopen("test.txt", "w");
    fprintf(fn,"111");
    fclose(fn);
}
Кнопки(Да, я там использовал глобальную переменную, которую и хотел изменять, её по сути можно удалить):
Код:
bool stoper=false;

void MainWindow::on_pushButton_clicked()//старт
{
    if(stoper==false)
    start(stoper);
}

void MainWindow::on_pushButton_2_clicked()//пауза
{   FILE* f;
    f=fopen("test.txt","w");

    if(f == NULL)
        {
            printf("не могу открыть файл '%s'", "test.txt");
        } else


        fprintf(f,"1");
    fclose(f);
}

void MainWindow::on_pushButton_3_clicked()//продолжить
{
    FILE* f;
        f=fopen("test.txt","w");

        if(f == NULL)
            {
                printf("не могу открыть файл '%s'", "test.txt");
            } else


            fprintf(f,"32");
        fclose(f);
}


Название: Re: Пауза
Отправлено: Пантер от Май 21, 2020, 23:02
>То есть файлом, можно игнорировать такую вещь, как потоки и передавать послания в любые другие потоки или основной

Угу. А про кеширование файловой системы ты не слышал? Обрабатываешь ситуацию, если файл пустой? А если двое сразу запишут в него что-нибудь?


Название: Re: Пауза
Отправлено: Hero Sanya от Май 21, 2020, 23:45
>То есть файлом, можно игнорировать такую вещь, как потоки и передавать послания в любые другие потоки или основной

Угу. А про кеширование файловой системы ты не слышал? Обрабатываешь ситуацию, если файл пустой? А если двое сразу запишут в него что-нибудь?
Это просто пример паузы. Переписывать файл могут только пауза и продолжить(просто в этом примере они активны одновременно, что ошибка).
На счёт игнорирования, я имел ввиду, что можно организовать общение потоков через файловую систему.
Да, тут нету обработок на случай если файл пустой, просто я в маинвиндовс, сразу создаю файл заполненным. Другое дело. если кто то целенаправленно захочет удалить или переписать файл(ну а это ровно тоже самое, если переписать любой файл программы).


Название: Re: Пауза
Отправлено: Igors от Май 22, 2020, 10:21
То есть файлом, можно игнорировать такую вещь, как потоки и передавать послания в любые другие потоки или основной. Можете назвать это костылём, но это работает идеально, так ещё и оставляет возможность для модификаций, например для сериализации кода(что пригодится для сохранения и загрузки).
Обычно подобные городушки лепятся для процессов (впрочем и там это плохо), но для потоков, где память общая, зачем же читать файл в цикле?
Цитировать
— Но зачем же брить яйца наголо, поручик?
— Дикари-с.

И это ровно то что делает QFutureInterface=)
Не слышал о таком, но рискну утверждать что для данной задачи (т.е. (при)остановить один но длинный расчет) он ничем не поможет. Т.к. задача по своей природе "интрузивна", т.е. ф-ция останова должна быть внедрена в код расчетов, сделать это чисто "извне" невозможно.

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


Название: Re: Пауза
Отправлено: Пантер от Май 22, 2020, 10:40
Соглашусь с Igors, для возможности останова должен быть модифицирован код. А любое добавление проверок или индикации в код расчета замедлит его в разы. Если возможно, можно разбить расчет на несколько этапов и делать проверки между этими этапами. Соответственно, в данном случае можно будет и сохранить текущее состояние расчетов.


Название: Re: Пауза
Отправлено: Авварон от Май 22, 2020, 10:45
А любое добавление проверок или индикации в код расчета замедлит его в разы. Если возможно, можно разбить расчет на несколько этапов и делать проверки между этими этапами. Соответственно, в данном случае можно будет и сохранить текущее состояние расчетов.

Если что, проверка на паузу -  это атомарная переменная с relaxed memory order. Если relaxed проверка тормозит расчёты то может стоит подумать о том что расчёты вообще не должны быть в потоке?=)


Название: Re: Пауза
Отправлено: Igors от Май 22, 2020, 12:55
А любое добавление проверок или индикации в код расчета замедлит его в разы.
Ну как.. это типовая проблема/ошибка - переборщить с индикатором. Но если делать аккуратно, то все норм, напр
Код
C++ (Qt)
std::atomic<int>prev, cur;
const int updateStep = 100;
 
#pragma omp parallel for
for (int i = 0; i < vec.size(); ++i) {
 DoCalc(vec[i]);
 ++cur;  
 #pragma omp master    // обновляем индикатор в главной нитке через каждые 100 итераций
 {
   if (cur - prev >= updateStep) {
     UpdateIndicator(cur - prev);
     prev = cur;
   }
 }
}

Если что, проверка на паузу -  это атомарная переменная с relaxed memory order. Если relaxed проверка тормозит расчёты то может стоит подумать о том что расчёты вообще не должны быть в потоке?=)
Собсно "проверка" - да, но нужно как-то поток остановить, и футуры здесь не помогут. И вообще, Вы прямо терроризируете начинающих.  "relaxed memory order" - бог весть что можно подумать, а это всего лишь самое обычное чтение переменной :) Ну если уж и оно тормозит...

И, кстати, как бум останавливать "по-нормальному" ? (sleep не есть хорошо). Давайте Вы на QWaitCondition,  а я на своем любимом QSemaphore


Название: Re: Пауза
Отправлено: Авварон от Май 22, 2020, 13:13
И, кстати, как бум останавливать "по-нормальному" ? (sleep не есть хорошо). Давайте Вы на QWaitCondition,  а я на своем любимом QSemaphore

Код:
void foo(QFutureInterface<int> promise)
{
    constexpr int iterations = 10000000;

    int result = 0;
    for (int i = 0; i < iterations; ++i) {
       if ((i % 1000) == 0 && promise.isPaused()) // relatively fast
          promise.waitForResume(); // slow, locks the mutex
       result += qHash(i);
    }
    promise.setResult(result);
}

А вы продолжайте писать велосипеды с семафором=)

Да, чтобы иметь возможность запускать через промис, надо потырить вот этот файлик из QtCreator, всячески рекомендую (его переписали на вариадики, кстати) https://code.woboq.org/qt5/qt-creator/src/libs/utils/runextensions.h.html

Запускать так:
Код:
#include "runextensions.h"

auto future = Utils::runAsync(foo);


Название: Re: Пауза
Отправлено: Igors от Май 22, 2020, 18:45
Код:
void foo(QFutureInterface<int> promise)
{
    constexpr int iterations = 10000000;

    int result = 0;
    for (int i = 0; i < iterations; ++i) {
       if ((i % 1000) == 0 && promise.isPaused()) // relatively fast
          promise.waitForResume(); // slow, locks the mutex
       result += qHash(i);
    }
    promise.setResult(result);
}

А вы продолжайте писать велосипеды с семафором=)

Да, чтобы иметь возможность запускать через промис, надо потырить вот этот файлик из QtCreator, всячески рекомендую (его переписали на вариадики, кстати) https://code.woboq.org/qt5/qt-creator/src/libs/utils/runextensions.h.html

Запускать так:
Код:
#include "runextensions.h"

auto future = Utils::runAsync(foo);
Ничего не понял :) iterations - это число задач/расчетов? Но в цикле Вы ничего не запускаете. Запуск где-то в др месте? Но в runAsync Вы подаете foo. Чему посвящен приведенный код, что он делает? Пока выглядит просто как бред собачий :)


Название: Re: Пауза
Отправлено: Пантер от Май 22, 2020, 18:56
Igors, перечитай код еще раз, он очень простой и все в нем нормально.


Название: Re: Пауза
Отправлено: Igors от Май 23, 2020, 07:33
Igors, перечитай код еще раз, он очень простой и все в нем нормально.
Перечитал, рез-т тот же :)

И вообще, в чем "хотелка"? Иметь удобные вызовы индикатор/пауза/отмена ? Все. что ли? Если так то эти мелкие подробности легче пристроить самому (если еще они понадобятся) чем забивать голову монструозными классами с мизерным ф-ционалом. Проблемы "разпоточивания" совсем в другом. В любой системе это ведь только так предполагается что есть/имеются N задач которые могут выполняться одновременно, на самом деле выделить/создать их часто очень непросто.


Название: Re: Пауза
Отправлено: Пантер от Май 23, 2020, 09:18
Igors, перечитай код еще раз, он очень простой и все в нем нормально.
Перечитал, рез-т тот же :)

Странно, этот код будет понятен даже джуну...

И вообще, в чем "хотелка"? Иметь удобные вызовы индикатор/пауза/отмена ? Все. что ли? Если так то эти мелкие подробности легче пристроить самому (если еще они понадобятся) чем забивать голову монструозными классами с мизерным ф-ционалом. Проблемы "разпоточивания" совсем в другом. В любой системе это ведь только так предполагается что есть/имеются N задач которые могут выполняться одновременно, на самом деле выделить/создать их часто очень непросто.

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


Название: Re: Пауза
Отправлено: Igors от Май 23, 2020, 10:17
Странно, этот код будет понятен даже джуну...
Думаю что нет. Ну если конечно джун не делает вид что, мол, ему все понятно :). Ну в самом деле
Код:
void foo(QFutureInterface<int> promise)
{
    constexpr int iterations = 10000000;

    int result = 0;
    for (int i = 0; i < iterations; ++i) {
       if ((i % 1000) == 0 && promise.isPaused()) // relatively fast
          promise.waitForResume(); // slow, locks the mutex
       result += qHash(i);
    }
    promise.setResult(result);
}
Не вижу где сами расчеты. Ну ладно, допустим они где-то в др месте, тогда выходит foo выполняется в главной нитке и только "забирает рез-т", наверно с помощью qHash. И что он должен делать/возвращать если задача еще не посчитана или вообще стоим на паузе?

И кто/как ставит на паузу? Наверно надо эту футуру подать в расчет, а там уж вызвать ее метод (а как иначе?). Опять только догадки, лучшего нет. А Вы говорите "все ясно"

Эта штука для того, чтобы каждый не пилил свой велосипед с незащищенным доступом к переменным, а юзал защищенные и быстрые вызовы.
Какие? Пауза что нужна в году раз и которую сделать 5 минут? И ради этого тащить немалый кусок темплейтовской срани? Та ну нафиг. И, кстати, вещь совсем не новая, но документировать не спешат, видимо сами "не удовлетворены"


Название: Re: Пауза
Отправлено: Авварон от Май 23, 2020, 13:21

И кто/как ставит на паузу? Наверно надо эту футуру подать в расчет, а там уж вызвать ее метод (а как иначе?). Опять только догадки, лучшего нет. А Вы говорите "все ясно"

В расчёт вы подаете promise. На паузу ставит футура:

Код:
#include "runextensions.h"

auto future = Utils::runAsync(foo);
future.pause();
future.resume();
future.waitForFinished();
int result = future.result();
qInfo() << result;


Название: Re: Пауза
Отправлено: Пантер от Май 23, 2020, 13:23
Хм, не думал, что все так туго...
Код
C++ (Qt)
result += qHash(i);
- это и есть весь расчет. Ты же не думал, что тебе тут напишут код расчета зависимости стоимости молока в средней полосе России от скорости сборки апельсинов во Вьетнаме. В данном примере показана логика использования данной схемы, не указан только момент останова, но, думаю, это будет всего-лишь проверка
Код
C++ (Qt)
if (promise.isCanceled()) return


Название: Re: Пауза
Отправлено: Igors от Май 25, 2020, 10:41
А! Т.е. qHash имитирует одну итерацию расчетв. Действительно, все просто, это я туплю :)

Ну хорошо, а что, QtConcurrent не обеспечивает этого ф-ционала (отмена/пауза)? (видел там подобные методы). Откуда возникла необходимость (или энтузиазм) юзать недокументированные фичи?


Название: Re: Пауза
Отправлено: Авварон от Май 25, 2020, 17:53
А! Т.е. qHash имитирует одну итерацию расчетв. Действительно, все просто, это я туплю :)

Ну хорошо, а что, QtConcurrent не обеспечивает этого ф-ционала (отмена/пауза)? (видел там подобные методы). Откуда возникла необходимость (или энтузиазм) юзать недокументированные фичи?

Функции  типа map(), reduce() умеют, а вот QtConcuent::run() не умеет, так как не передает QFutureInterface в юзерскую функцию. Вот этот хедер из QtCreator'а делает ровно то же самое, что делает QtConcuent::run(), только первым параметром передает QFutureInterface. Это в нем юзается повсеместно - например, когда парсится проект и бежит прогрессбар справа в углу - это вот оно (да, QFutureInterface еще умеет и прогресс репортить).


Название: Re: Пауза
Отправлено: alexu007 от Май 29, 2020, 23:07
Пауза:

Код
C++ (Qt)
void Widget::pause(int ms)
{
   QEventLoop el;
   QTimer t;
   connect(&t, SIGNAL(timeout()), &el, SLOT(quit()));
   t.start(ms);
   el.exec();
}