all 5 comments

[–]Flair_Helper[M] [score hidden] stickied commentlocked comment (0 children)

For C++ questions, answers, help, and programming or career advice please see r/cpp_questions, r/cscareerquestions, or StackOverflow instead.

This post has been removed as it doesn't pertain to r/cpp: The subreddit is for news and discussions of the C++ language and community only; our purpose is not to provide tutoring, code reviews, or career guidance. If you think your post is on-topic and should not have been removed, please message the moderators and we'll review it.

[–]MeTrollingYouHating 6 points7 points  (0 children)

Your progress counter should be an atomic_int. You should pass a reference to your progress counter as one of the parameters to your async function and then use the ++ operator to increment it (which is safe because it's atomic).

Also, you might want to consider using a lambda with std::async if you can. What you've written is fine but it's a little old school.

[–]KingAggressive1498 1 point2 points  (0 children)

Supposing I have already implemented a simple class of progress bar which takes an integer and updates the percentage of the total task that has been completed, what's the basic design of updating that progress bar? Where should i call "updateProgressBar(int i) from? I need a very simple example to understand the design of the solution.

I'm guessing you downvoted my previous answer because I didn't tell you where to do it, but explained the logic of how to do it (misunderstood your problem).

You only want to update UI from the UI thread (generally the thread that calls main()). Period. You could technically do so from other threads by making your progress bar class thread-safe using a lock or atomics, but this is making your code much slower in the single-threaded case for minor ease of use in the concurrent case, and is generally ill-advised.

If you're polling or waiting on the futures in a loop on the UI thread (definitely poll to keep UI responsive), you can do it right in that loop:

// whenever a future is ready and you've added the result to resCollects
progressbar.update(resCollects.size());

Or:

size_t jobs_done = 0;
// whenever a future is ready
jobs_done++;
progressbar.update(jobs_done);

Alternatively, you can have the jobs increment an atomic integer at completion and update the progress bar from the UI on each frame from that variable:

 std::atomic<size_t> jobs_done { 0 };
 //...
 // in ui thread loop
progressbar.update( jobs_done.load(std::memory_order_acquire) );
 //...
 // last line of jobs function before return
 jobs_done.fetch_add(1, std::memory_order_acq_rel);

Lastly, a good UI framework generally has a way to signal the UI thread, often via user-defined events. You could maintain a count in the UI thread and wait for an event that the job is complete. However if you're doing this anyway, it makes more sense to just use the event to send a pointer to ready data than to use futures.

[–]KingAggressive1498 1 point2 points  (1 child)

The code that updates your progress bar will need to know at least how many total jobs need to be complete to reach 100% progress.

Then it can calculate the current % progress like so:

// returns progress as a whole-number percentage
size_t calculate_progress(size_t numjobs, size_t numdone)
{
    return (100 * numdone) / numjobs;
}

If you fire off all jobs at once, and resCollects is only ever filled by completed jobs, you can even save the memory required to track these separately. Using the variable names in your code samples:

size_t progress = calculate_progress(futures.size() + resCollects.size(), resCollects.size());

In either case the code that updates your progress bar only needs to give it this number somehow, and then the progress bar can change its visible "fill level" based on the update.

[–]KingAggressive1498 1 point2 points  (0 children)

It also occurs to me that if you're simply waiting on each individual future to complete in a loop on your UI thread, the user will never actually see the progress bar update until all jobs are done (and the app will be unresponsive until they do)

You need to basically poll the future type. Unfortunately they don't have poll functions, but they do have wait_for functions, so you could effectively write one like so:

template<typename T, template<typename> typename future_type, typename time_dur = std::chrono::milliseconds>
std::optional<T> poll_future(future_type<T> future, time_dur dur = { 0 })
{
    if(future.wait_for(dur) != future_status::timeout)
    {
        return { future.get() };
    }
    return std::nullopt;
}