【问题标题】:Threaded Building Blocks (TBB) ```enqueue``` task lifetime线程构建块 (TBB) ```入队``` 任务生命周期
【发布时间】:2014-03-13 19:35:19
【问题描述】:
如果你在函数中使用tbb::task::enqueue 将任务入队,然后在任务执行之前函数超出范围,任务会丢失吗?
如果是这样,如何避免?例如,如果你想在一个短暂的事件处理回调中加入一个任务,该回调将很快超出范围,而该任务直到稍后才会被调度程序执行?
此外,enqueue 函数是否有容量限制?如果有超过一定数量的待处理任务,它会丢弃吗?
【问题讨论】:
标签:
c++
multithreading
c++11
concurrency
tbb
【解决方案1】:
tbb::task 是一个对象。与任何其他 C++ 对象一样,相同的 C++ 生命周期规则(和危险!)适用于 tbb::task。对于有问题的情况,请确保以不受函数返回影响的方式捕获任务中的信息。例如,捕获局部变量的值,而不是引用。
这是一个使用 lambda 表达式显示问题的程序。借用Alexey Kukanov's lambda_task
#include <tbb/tbb.h>
template<typename F>
class lambda_task : public tbb::task {
F my_func;
/*override*/ tbb::task* execute() {
my_func();
return NULL;
}
public:
lambda_task( const F& f ) : my_func(f) {}
};
template<typename F>
void tbb_enqueue_lambda( const F& f ) {
tbb::task::enqueue( *new( tbb::task::allocate_root() ) lambda_task<F>(f) );
}
void LaunchOneTask( int i, int j ) {
if( i%1000000==0 )
[i,&j]{printf("Launching i=%d j=%d\n",i,j);}();
tbb_enqueue_lambda( [i,&j]{ // Deliberate mistake for j!
printf("Hi from lambda: i=%d j=%d\n",i,j);
sleep(1);
} );
}
int main() {
for( int i=0; i<1000000000; ++i ) {
LaunchOneTask(i,i);
}
}
如果你运行它,你会看到“Launching...”行打印i 和j 正确,但“Hi from...”行打印错误 j 的值。这是因为 lambda 已经通过引用 (&j) 捕获了 j,并且该引用是对一个在任务运行之前消失的左值。
据我所知,tbb::task::enqueue的容量限制是系统内存的容量限制。由程序员来确保不会发生这种情况。