我正在嘗試實作使用執行緒在螢屏上列印多個進度條的功能。我有一個ProgressBar生成進度條的類(如果你需要更多關于它的資訊,你可以看這里,互斥鎖已被洗掉),它有一個update方法來更新回圈內的進度條。
然后,我正在創建一個新MultiProgressBar類來管理多個帶有執行緒的進度條并在終端中同時顯示它們。
#ifndef MULTIPROGRESSBAR_H
#define MULTIPROGRESSBAR_h
#include <iostream>
#include <type_traits>
#include <tuple>
#include <functional>
#include <mutex>
#include <atomic>
#include <utility>
namespace osm
{
template <size_t... Is>
struct indices {};
template <size_t N, size_t... Is>
struct gen_indices: gen_indices <N - 1, N - 1, Is...> {};
template <size_t... Is>
struct gen_indices <0, Is...>: indices<Is...> {};
template <class... Indicators>
class make_MultiProgressBar
{
public:
template <class... Inds>
make_MultiProgressBar( Inds&&... bars ): bars_{ std::forward <Inds> ( bars )... } {}
static size_t size() { return sizeof...( Indicators ); }
template <class Func, class... Args>
void for_one( size_t idx, Func&& func, Args&&... args )
{
call_one( idx, gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
}
template <class Func, class... Args>
void for_each( Func&& func, Args&&... args )
{
call_all( gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
}
private:
template <size_t... Ids, class Func, class... Args>
void call_one( size_t idx, indices <Ids...>, Func func, Args&&... args )
{
std::lock_guard<std::mutex> lock{mutex_};
[](...) {}
(
(idx == Ids &&
( ( void ) std::forward <Func> ( func )( std::get <Ids> ( bars_ ),
std::forward <Args> ( args )... ), false ) )...
);
}
template <size_t... Ids, class Func, class... Args>
void call_all( indices <Ids...>, Func func, Args&&... args )
{
auto dummy = { ( func( std::get <Ids>( bars_ ), args...), 0 )... };
( void )dummy;
}
std::tuple <Indicators&...> bars_;
std::mutex mutex_;
};
template <class... Indicators>
make_MultiProgressBar <typename std::remove_reference <Indicators>::type...>
MultiProgressBar( Indicators&&... inds )
{
return { std::forward <Indicators> ( inds )... };
}
template <class T>
struct type_identity
{
using type = T;
};
struct updater
{
template <template <class> class PB, class bar_type>
auto operator()( PB <bar_type>& pb, typename type_identity <bar_type>::type v ) const
-> decltype( pb.update( bar_type{} ) )
{
return pb.update( v );
}
};
}
#endif
并且在主程式中應該作為:
#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <iomanip>
#include "../include/osmanip.h" //Header containing ProgressBar class and others.
using namespace osm;
using namespace std;
using namespace std::this_thread;
using namespace std::chrono;
ProgressBar<int> prog_int;
prog_int.setMin( 0 );
prog_int.setMax ( 100 );
prog_int.setStyle( "complete", "%", "#" );
prog_int.setBrackets( "[", "]" );
ProgressBar<int> prog_int_2;
prog_int_2.setMin( 5 );
prog_int_2.setMax ( 25 );
prog_int_2.setStyle( "complete", "%", "#" );
prog_int_2.setBrackets( "[", "]" );
ProgressBar<float> prog_float;
prog_float.setMin( 0.1f );
prog_float.setMax ( 12.1f );
prog_float.setStyle( "complete", "%", "#" );
prog_float.setBrackets( "[", "]" );
auto bars = MultiProgressBar( prog_int, prog_int_2, prog_float );
// Job for the first bar
auto job1 = [&bars]() {
for (int i = 0; i <= 100; i ) {
bars.for_one(0, updater{}, i);
sleep_for( milliseconds( 100 ) );
}
cout << endl;
};
// Job for the second bar
auto job2 = [&bars]() {
for (int i = 5; i <= 25; i ) {
bars.for_one(1, updater{}, i);
sleep_for(std::chrono::milliseconds(200));
}
cout << endl;
};
// Job for the third bar
auto job3 = [&bars]() {
for (float i = 0.1f; i <= 12.1f; i = 0.1f) {
bars.for_one(2, updater{}, i);
sleep_for(std::chrono::milliseconds(60));
}
cout << endl;
};
thread first_job(job1);
thread second_job(job2);
thread third_job(job3);
first_job.join();
second_job.join();
third_job.join();
問題是當我運行代碼時,進度條會重疊列印,如下所示:
0 [00:53] gianluca@ubuntu:~/osmanip (main)$ ./bin/main.exe
[## ] 9.166668%
相反,我想要類似的東西:
0 [00:53] gianluca@ubuntu:~/osmanip (main)$ ./bin/main.exe
[############ ] 45%
[######### ] 39%
[################## ] 72%
我在這個例子中發現一個解決方案可能是在類中使用一個std::atomic<bool>變數MultiProgressBar并且當它True向上移動游標時,否則不會做任何事情,就像在這個例子中,從我放的鏈接(這是定義一個MultiProgressBar類的方法來呼叫其他單個進度條的update方法,這里稱為):write_progress
public:
//Some code...
//...
void write_progress(std::ostream &os = std::cout) {
std::unique_lock lock{mutex_};
// Move cursor up if needed
if (started_)
for (size_t i = 0; i < count; i)
os << "\x1b[A";
// Write each bar
for (auto &bar : bars_) {
bar.get().write_progress();
os << "\n";
}
if (!started_)
started_ = true;
}
//Some code...
//...
private:
// [...]
std::mutex mutex_;
std::atomic<bool> started_{false};
但是我不明白我應該在哪里以及如何在我的班級中放置一個std::atomic<bool>變數。MultiProgressBar抱歉,我還在學習如何使用執行緒。有人能幫我嗎?
uj5u.com熱心網友回復:
問題是每個條都會擦除該行,但在執行之前不會更新其行,因此每次都只是擦除同一行。在你鏈接的帖子中,MultiBar班級負責擦除和寫作,所以你的也必須這樣做。在您的情況下,每個條從for_one方法中擦除和寫入自身,您MultiProgressBar必須至少在呼叫條的更新之前更新行的游標。這是執行此操作的偽代碼:
updateOneProgressBar(barIndex){
rowDiff = lastUpdatedBarIndex - barIndex
if(rowDiff < 0) // move upwards
moveCursorUp(-rowDiff)
else // move downwards
moveCursorDown(rowDiff)
// now the cursor is in the correct row
eraseAndRewriteBar(barIndex)
// don't forget to update the control variable
lastUpdatedBarIndex = barIndex
}
uj5u.com熱心網友回復:
如果不使用某種控制臺圖形庫,您將無法做到這一點,因為無法向上移動游標以寫入前一行。您將需要類似curses或 Win32 控制臺 API 之類的東西SetConsoleCursorPos和朋友。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/425975.html
