ОБНОВЛЕНИЕ В ответ на комментарий вот простая реализация классов progress_group
и group_progress_display
, которые вместе упрощают отображение одного индикатора выполнения для нескольких различных элементов выполнения (экземпляров progress_group::item
).
Смотрите Прямой эфир на Coliru.
Посмотрим на progress_group
:
struct progress_group {
struct item {
size_t current, total;
item(size_t total=100, size_t current=0)
: current(current), total(total) { }
void tick() {
if (current < total) current++;
}
};
std::list<boost::weak_ptr<progress_group::item> > members;
void add(boost::shared_ptr<item> const& pi) {
assert(pi);
members.push_back(pi);
}
item get_cumulative() {
item cumul(0, 0);
for(auto& wpi : members) {
auto pi = wpi.lock();
if (pi) {
cumul.current += pi->current;
cumul.total += pi->total;
}
}
return cumul;
}
};
Обратите внимание, что я (произвольно) решил использовать слабые указатели, поэтому элементы прогресса могут исчезнуть, а масштаб будет просто скорректирован.
Фактический прогресс динамически масштабируется в соответствии с разрешением базового виджета отображения (boost::progress_display
). Основное ограничение, которое это оставляет, заключается в том, что прогресс должен строго увеличиваться с течением времени (поскольку вывод может быть не на tty):
struct group_progress_display {
group_progress_display() : _display(1000), _reentrancy(0) {
}
void add(boost::shared_ptr<progress_group::item> pi) {
_group.add(pi);
}
void update() {
if (1 == ++_reentrancy) // cheap synch
{
auto cumul = _group.get_cumulative();
if (cumul.total > 0)
{
size_t target = (1.0 * cumul.current)/cumul.total * _display.expected_count();
if (target >= _display.count())
_display += target - _display.count();
}
}
--_reentrancy;
}
private:
boost::progress_display _display;
progress_group _group;
boost::atomic_int _reentrancy;
};
В этом образце выполняется 100 фоновых заданий с различной нагрузкой в потоках и отображается один виджет кумулятивного хода выполнения:
int main()
{
boost::thread_group workers;
group_progress_display display;
for (int i = 0; i < 100; ++i)
{
auto load = (rand()%5) * 1500;
auto progress_item = boost::make_shared<progress_group::item>(load);
display.add(progress_item);
worker this_worker(progress_item->total);
this_worker.attachListener([=,&display]{
progress_item->tick();
display.update();
});
workers.create_thread(this_worker);
}
workers.join_all();
}
(Обратите внимание, как лямбда-выражение (или выражение boost::bind
для c++03) само поддерживает общий указатель.)
worker
ничего не знает о реализации прогресса:
struct worker {
explicit worker(size_t count) : count(count) {}
template <typename L>
void attachListener(L&& listener) {
m_callback = std::forward<L>(listener);
}
void operator()()
{
for (size_t i = 0; i < count; ++i) {
boost::this_thread::sleep_for(boost::chrono::microseconds(500));
m_callback();
}
}
private:
boost::function<void()> m_callback;
size_t count;
};
Старый ответ
Похоже, это работает Live On Coliru.
Потенциальная проблема может заключаться в том, что в данный момент индикатор выполнения каким-то образом должен иметь точную информацию об этапах процесса. Вы можете захотеть скрыть эту информацию (внутри данных или обернуть ее в класс, который также добавляет информацию о ходе выполнения), а затем, используя некоторые простые арифметические действия, вы можете преобразовать их в фиксированную шкалу (скажем, 100).
Это позволит даже вашему Foo::doWork
регулировать масштаб на лету, что в значительной степени является доказательством того, что развязка работает.
person
sehe
schedule
17.06.2014
boost::progress_display
? Я никогда не видел его раньше (уоу) - person sehe   schedule 17.06.2014