Как правильно делать прерываемые подпрограммы
От: GhostCoders Россия  
Дата: 13.02.18 11:47
Оценка:
а также подпрограммы с показом текущео прогресса в %?

Есть некая подпрограмма, которая делает что-то полезное и долго.

Как ее безопасно отменить выполнение этой подпрограммы?

псевдо-код:
void process_school(School& school)
{
    for(class: school.classes())
    {
        process_class(class);
    }
}
void process_class(Class& class_)
{
    for(student: class_)
    {
        process_student(student);
    }
}
void process_stundent(Student& student)
{
    // ...
}


Скажем, в школе 1000 классов, и в кажом до 50 студентов. Время на обрабоку каждого студента — 10 сек.
Вообщем, функция (или метод) process_school() будет работать долго.

Клиент запускает эту подпрограмму в отдельном потоке.
Как безопасно реализовать прерываемость это подпрограммы?
Какие существуют общие подходы для этого?

Пока это реализовано при помощи класса хелпера, при помощи коллбэка (превдокод, вопрос в комментариях):
struct ICallback
{
    virtual bool ReportProgress(int percent) = 0; // На вход принимает текущий прогресс в %, на выходе true - продолжать процесс, false - завершить
};

ICallback* g_callback;

class progress_helper
{
    int m_expected_steps; // Ожидаемое число шагов в текущей подзадачи
    int m_current_step; // Текущий шаг в текущей подзадачи
public:
    int calculate_current_total_progress(); // Некий метод, который вычисляет и возвращает текущий прогресс (число от 0 до 100%)
    progress_helper(int expected_steps) : m_expected_steps(expected_steps), m_current_step(0)
    {
        if (!g_callback.ReportProgress(calculate_current_total_progress()))
        {
            throw std::runtime_error("required job abort"); // Или использовать свой специальный тип JobAbort
        }
    }
    void step()
    {
        ++m_current_step;
        update_current_progress(); // Некий метод, который обновляет текущий прогресс (число от 0 до 100%)
        if (!g_callback.ReportProgress(calculate_current_total_progress()))
        {
            throw std::runtime_error("required job abort"); // Или использовать свой специальный тип JobAbort
        }
    }
};

void process_school(School& school)
{
    progress_helper school_progress(school.get_class_count());
    for(class: school.classes())
    {
        process_class(class);
        school_progress.step();
    }
}
void process_class(Class& class_)
{
    progress_helper class_progress(school.get_student_count());
    for(student: class_)
    {
        process_student(student);
        class_progress.step();
    }
}
void process_stundent(Student& student)
{
    progress_helper student_progress(3);
    process_name(student.name);
    student_progress.step();
    process_surname(student.surname);
    student_progress.step();
    process_sex(student.sex); // :)
    student_progress.step();
}
// На самом деле создание классов progress_helper на стеке может
// каскадироваться, то есть, например, если в школе 10 классов,
// то progress_helper (в методе process_school) будет "тикать" 10 шагов,
// каждому шагу будет соответствовать 10% прогресса
// В функции process_class при создании progress_helper он уже будет
// "тикать" в диапозоне 10%, то есть первый раз от 0% до 10%, второй раз от 10% до 20% и так далее.
// Текущий диапозон прогресса дробится на число ожидаемых шагов, в хелперы вложенных функций уже работают 
// с текущим диапозоном "програсса" и делят уже его.
// Но то, как вычисляется прогрессс - это не суть важно.
// Важно то, что при вызове конструктора или метода step() progress_helper при помощи коллбэка (тоже не важно какого- указатель на виртуальный интерфейс, или лямбда)
// если в коллбэке решили (то есть на вызывающей стороне) - что хватит, пора закруглятся и вернули false,
// то выбрасывается некое исключение, и текущий поток заверщается естественным обраов (с перехватом ожидаемого исключения JobAbort).
// Не нужно в таком случае "убивать" поток жекстко при помощи средств ОС.
// Минус этого подхода в том, что нужно расставлять локальные переменные типа progress_helper на стеке каждой функции.
// Какие уже есть опробированные решения для этого?
Третий Рим должен пасть!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.