Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2425 Discussions

pause/resume of task - control of system load


I need twos features that I'm currently unable to find in TBB:

- a way to reduce (at runtime, and during a task execution) the number of threads used.
- a way to pause the execution of my tasks (or allocate 0 threads to the current task)

The idea behind that is that the user of my tool must be allowed to pause the execution if he wants to free its processor for another task. Or he may decide to only allocate 4 of its 8 cores to this task.

Because my task can take a while to end, I need to be able to do this change "in the middle of a task".

I'm currently using task_scheduler_init between the creation of task, but because it waits for the end of task to take effect, the effect can take too long.

Is there a way to do this inside TBB?

Thank you.
0 Kudos
3 Replies
Black Belt
Create a parking task that each thread is likely to fall through (as a task).

The task is a NOP task except when a thread must be suspended. At that time the thread issues a Sleep or WaitForSingleEvent or condition variable (or you choose your own medicine).

This may require a non-TBB thread to query and modify the state variables (desired number of threads).
This parking task needs to be periodically scheduled during your normal running of the app. This task could get scheduled only when the state variable indicates an additional thread needs to be suspended. This won't provide instanteanous suspension.

Jim Dempsey
Black Belt
"Create a parking task that each thread is likely to fall through (as a task)."
Hmm... that still won't prevent one or more tasks' getting trapped on the stack in the middle of its/their execution, so you might as well keep track of the current set using task_scheduler_observer() and suspend them directly for immediate effect. I hope some more work gets done in that general area for TBB to play nice with other threading models (although that goes both ways!).
Thank for your advices.

I defined an API like that:

[cpp]#ifndef TBB_PAUSE_H #define TBB_PAUSE_H /* * tbb/compat/condition_variable provides the std::unique_lock and * std::condition_variable of C++ 2011 in STD namespace */ #include "tbb/tbb.h" #include "tbb/compat/condition_variable" class Scheduler { public: Scheduler(unsigned m): max_thread_count(m), current_thread_count(0) {} void SetNumThread(int num) { std::unique_lock<:MUTEX> lock(mutex); int delta = num - max_thread_count; // Wake up sleeping threads if(delta > 0) for(int i = 0; i < delta; ++i) condition.notify_one(); max_thread_count = num; } private: friend class Pause; // Called when a thread starts a range void Begin() { std::unique_lock<:MUTEX> lock(mutex); while(current_thread_count >= max_thread_count) condition.wait(lock); current_thread_count++; } // At the end of a range void End() { std::unique_lock<:MUTEX> lock(mutex); current_thread_count--; // this test is not useful, but avoid a thread wakeup for nothing if(current_thread_count < max_thread_count) condition.notify_one(); } private: tbb::mutex mutex; std::condition_variable condition; int max_thread_count, current_thread_count; }; // Pause object, you must create one at the beginning of your task class Pause { public: Pause(Scheduler * const sched): scheduler(sched) { scheduler->Begin(); } ~Pause() { scheduler->End(); } private: Scheduler * const scheduler; }; #endif [/cpp]
And it can be used like that:

#include "tbb_pause.h"

Scheduler* scheduler;

// A simple class
void mytask(const tbb::blocked_range&range)
// Create the pause object
Pause pause(scheduler);

for(size_t i = range.begin(); i != range.end(); ++i)
// Do useless work
int sum = 0;
for(unsigned int j = 0; j < 100; j++)
sum += j;

void set_pause()
// Pause after one seconds

// pause of ten seconds, resume with one threads


int main(int, char** argv)
Scheduler sched(8);
scheduler = &sched;

//boost::thread t(set_pause);

tbb::parallel_for(tbb::blocked_range(0, atoi(argv[1])), &mytask);

The idea is that any blocked_range executed by TBB can create a scoped Pause instance which will control the number of threads. I can pause/resume by setting the number of threads to zero.

I'm pretty sure this API is not recursive (IE, a task cannot create another task), but it may work for my purpose.

Thank for your help, and if you come with any random remarks about this piece of code, I'll be rather happy.