Здравствуйте, GhostCoders, Вы писали:
GC>а также подпрограммы с показом текущео прогресса в %?
GC>Есть некая подпрограмма, которая делает что-то полезное и долго.
Думаю что следует изменть подход к задаче.
Следует составлять план задачи которая будет выполняться. Тогда и время проще оценивать, можно будет остановить и возобновить обработку.
Еще вместо процентов лучше использовать долю выполнения от 0 до 1.
| оценка времени |
| #include <math.h>
struct Disp {
void reset() { m=d=0; n=0; }
void add(double x) { n+=1; if (n>1) d=d*(n-2)/(n-1)+(x-m)*(x-m)/n; m+=(x-m)/n; }
double getN() const { return n; } // кол-во n
double getM() const { return m; } // среднее sum(x[i])/n
double getD() const { return d; } // оценка дисперсии sum((x[i]-mean)^2)/(n-1)
Disp() { reset(); }
private:
double n,m,d;
};
// оценка времени оставшегося до v=1 от момента t
// LinearEstimator le;
// le.add(0,0.0);
// le.add(1,0.1);
// le.add(2,0.2);
// le.add(3,0.3);
// double remains=le.estimate_timeleft(7); // remains = 3
struct LinearEstimator {
void reset();
void add(double t,double v);
double estimate_timeleft(double t,double *err=0,double v=1.0); // -1 if no assumption
LinearEstimator() { reset(); }
protected:
double A00,A01,A11,b0,b1,b2,lt,lv;
double SS,c1,c0; Disp disp;
void update(double t,double v);
};
//-----------------------------------------------------------------------------
void LinearEstimator::reset() {
A00=A01=A11=b0=b1=b2=lt=lv=0;
SS=c1=c0=0; disp.reset();
}
void LinearEstimator::add(double t,double v) {
A00+=1; A01+=t; A11+=t*t;
b0+=v; b1+=v*t; b2+=v*v;
lt=t; lv=v; update(t,v);
}
void LinearEstimator::update(double t,double v) {
if (A00<=1) return;
double d=A00*A11-A01*A01; //if (fabs(d)<EPS) return;
double a0=(b0*A11-b1*A01)/d, a1=(A00*b1-A01*b0)/d;
double d0=a0-c0, d1=a1-c1, t1=v-c1*t-c0, t2=d1*t+d0, t3=A11*d1*d1+2*A01*d0*d1+A00*d0*d0;
SS+=t1*(t1-2*t2)+t3; c1=a1; c0=a0;
}
double LinearEstimator::estimate_timeleft(double t,double *err,double v) { // -1 if no assumption
if (err) *err=-1; if (lv>=v || A00<2) return -1;
double d=A00*A11-A01*A01; //if (fabs(d)<EPS) return -1;
double a0=(b0*A11-b1*A01)/d, a1=(A00*b1-A01*b0)/d; //if (fabs(a1)<EPS) return -1;
double x=(v-a0)/a1, left=x-t;
if (err) {
disp.add(x);
if (A00>2) {
double q=sqrt(fabs(SS/(A00-1)))/a1, p=sqrt( disp.getD() );
*err=hypot( q, p );
}
}
return left;
}
|
| |
а также подпрограммы с показом текущео прогресса в %?
Есть некая подпрограмма, которая делает что-то полезное и долго.
Как ее безопасно отменить выполнение этой подпрограммы?
псевдо-код:
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 на стеке каждой функции.
// Какие уже есть опробированные решения для этого?
Но предложенный подход (с ручным шаганием по циклам и сохранением состояния обхода между вызовами шага) тоже можно, и обычно даже предпочтительнее.
Здравствуйте, loginx, Вы писали:
L>а что если запускать ее в виртуальной машине и управлять старт/стоп/пауза/возобновить
L>процессом через тот API и те средства что есть у виртуальной машины?
Ну, можно на C# написать — там есть yield return
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, loginx, Вы писали:
L>>а что если запускать ее в виртуальной машине и управлять старт/стоп/пауза/возобновить
L>>процессом через тот API и те средства что есть у виртуальной машины?
MD>Ну, можно на C# написать — там есть yield return
это не то, как ты их внедришь внутрь чужой мат. либы нэйтивной — речь же о сложных длительных вычислениях