In the Intel thread building blocks framework, how does one ensure that all threads are not busy waiting for other threads to complete.
Consider for example the following code,
#include <tbb/tbb.h>
#include <vector>
#include <cstdlib>
#include <future>
#include <iostream>
std::future<bool> run_something(std::function<bool(bool)> func, bool b) {
auto task = std::make_shared<std::packaged_task<bool()> >(std::bind(func, b));
std::future<bool> res = task->get_future();
tbb::task_group g;
g.run([task]() { (*task)(); });
return res;
};
int main() {
tbb::parallel_for(0, 100, 1, [=](size_t i) {
g.run([] () {
std::cout << "A" << std::endl;
run_something([] (bool b) { return b; }, true).get();
});
});
return EXIT_SUCCESS;
}
Here the main
function spawns as tasks as there are threads in the thread pool used by the TBB library. Then when the second call to spawn more tasks happens in the run_something
function, the TBB scheduler sees that no threads are available and simply deadlocks. That is I see that that print statement goes through exactly 4 times on a 4 hyper thread machine and 8 times on a 8 hyper thread machine.
How do I avoid this scenario, in particular, is there a way to ensure that two task_group
or task_arena
or parallel_for
constructs use two completely disjoint set of threads?
The std::future
is not compatible with TBB's optional parallelism paradigm. The std::future::get()
should really be named as let_me_block_in_system_wait_here()
. Unless you want TBB to deadlock, it is prohibited to implement any kind of synchronization between TBB tasks which is not known to TBB task scheduler. That is, express dependencies between your TBB tasks using TBB means.
The optional parallelism means that your code has to be correct running using just a single thread only. Only tbb::task::enqueue()
gives you a promise of at least one worker in addition to master thread.
Your code should not even compile since you use g.run()
in main()
without declaring g
. And it is prohibited to destroy task_group
before the call to wait()
as stated in the reference for the destructor: Requires: Method wait must be called before destroying a task_group, otherwise the destructor throws an exception.
As for the shared thread pool. Yes, TBB has one shared thread pool. But you can control the way how the work is shared using task_arena
. No task can leave arena but the worker threads can migrate across arenas in the time between running the tasks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With