使用TBB重置类成员时出现编译错误的问题咨询
问题描述
我用TBB并行加载数据到队列,load_data方法运行正常。现在编写cancel_load方法,需要取消加载进程,重置arena和task以便重新加载,加载停止后用clear_queue清空队列。task.cancel()能正常执行,但尝试用新实例重置arena和task时,编译报“error: use of deleted function”错误。为什么会这样?我没删除task对象,只是想用新实例重置,有没有更好的重置类成员的方式?
#include <iostream> #include <tbb/concurrent_queue.h> #include <tbb/task_group.h> #include <tbb/tbb_thread.h> class ParallelDataset { public: tbb::concurrent_bounded_queue<record_data::Record> MyQueue; tbb::task_arena arena; tbb::task_group task; ~ParallelDataset() { task.wait(); } void load_() { // start loading data into MyQueue } void load_data() { arena.execute([&]() { task.run([&]() { load_(); }); }); } void cancel_load() { task.cancel(); tbb::task_arena new_arena; tbb::task_group new_task; arena = new_arena; task = new_task; std::cout << "Data loading is cancelled. Arena and Task have been reset" << std::endl; } void clear_queue() { MyQueue.clear(); } };
编译错误信息
/home/src/dataset.cpp: 在成员函数‘void ParallelDataset::cancel_load()’中: /home/src/dataset.cpp:275:16: 错误:使用了被删除的函数‘tbb::task_group& tbb::task_group::operator=(const tbb::task_group&)’ 275 | task = new_group; | ^~~~~~~~~ 从/usr/include/tbb/task_group.h:192包含的文件: /usr/include/tbb/task_group.h:192:7: 注意:‘tbb::task_group& tbb::task_group::operator=(const tbb::task_group&)’被隐式删除,因为默认定义会形成错误: 192 | class task_group : public internal::task_group_base { | ^~~~~~~~~~ /usr/include/tbb/task_group.h:192:7: 错误:使用了被删除的函数‘tbb::internal::task_group_base& tbb::internal::task_group_base::operator=(const tbb::internal::task_group_base&)’ /usr/include/tbb/task_group.h:92:7: 注意:‘tbb::internal::task_group_base& tbb::internal::task_group_base::operator=(const tbb::internal::task_group_base&)’被隐式删除,因为默认定义会形成错误: 92 | class task_group_base : internal::no_copy { | ^~~~~~~~~~~~~~~ /usr/include/tbb/task_group.h:92:7: 错误:使用了被删除的函数‘tbb::internal::no_copy& tbb::internal::no_copy::operator=(const tbb::internal::no_copy&)’ 从/usr/include/tbb/task.h:23包含的文件, 从/usr/include/tbb/parallel_for.h:24, 从/home/src/dataset.cpp:4: /usr/include/tbb/tbb_stddef.h:330:7: 注意:‘tbb::internal::no_copy& tbb::internal::no_copy::operator=(const tbb::internal::no_copy&)’被隐式删除,因为默认定义会形成错误: 330 | class no_copy: no_assign { | ^~~~~~~ /usr/include/tbb/tbb_stddef.h:330:7: 错误:使用了被删除的函数‘void tbb::internal::no_assign::operator=(const tbb::internal::no_assign&)’ /usr/include/tbb/tbb_stddef.h:324:10: 注意:在此声明 324 | void operator=( const no_assign& ) = delete; | ^~~~~~~~ 从/home/src/dataset.cpp:6包含的文件: /usr/include/tbb/task_group.h:92:7: 错误:使用了被删除的函数‘tbb::task_group_context& tbb::task_group_context::operator=(const tbb::task_group_context&)’ 92 | class task_group_base : internal::no_copy { | ^~~~~~~~~~~~~~~ 从/usr/include/tbb/parallel_for.h:24包含的文件, 从/home/src/dataset.cpp:4: /usr/include/tbb/task.h:347:7: 注意:‘tbb::task_group_context& tbb::task_group_context::operator=(const tbb::task_group_context&)’被隐式删除,因为默认定义会形成错误: 347 | class task_group_context : internal::no_copy { | ^~~~~~~~~~~~~~~~~~ /usr/include/tbb/task.h:347:7: 错误:使用了被删除的函数‘tbb::internal::no_copy& tbb::internal::no_copy::operator=(const tbb::internal::no_copy&)’
原因分析
TBB的task_group和task_arena类继承自tbb::internal::no_copy,这个基类显式删除了赋值运算符(operator=),所以你不能直接用赋值语句arena = new_arena或task = new_task来重置对象——这类对象是不可赋值、不可拷贝的,编译器会因为尝试调用被删除的赋值运算符报错。
解决方案
有两种常用方式处理这类不可拷贝赋值的成员重置:
1. 使用智能指针包裹成员
把arena和task改成std::unique_ptr,重置时直接创建新对象替换指针即可:
#include <iostream> #include <memory> #include <tbb/concurrent_queue.h> #include <tbb/task_group.h> #include <tbb/tbb_thread.h> class ParallelDataset { public: tbb::concurrent_bounded_queue<record_data::Record> MyQueue; std::unique_ptr<tbb::task_arena> arena; std::unique_ptr<tbb::task_group> task; ParallelDataset() : arena(std::make_unique<tbb::task_arena>()), task(std::make_unique<tbb::task_group>()) {} ~ParallelDataset() { if (task) { task->wait(); } } void load_() { // start loading data into MyQueue } void load_data() { arena->execute([&]() { task->run([&]() { load_(); }); }); } void cancel_load() { task->cancel(); task->wait(); // 必须等待任务完全终止后再重置 arena = std::make_unique<tbb::task_arena>(); task = std::make_unique<tbb::task_group>(); clear_queue(); std::cout << "Data loading is cancelled. Arena and Task have been reset" << std::endl; } void clear_queue() { MyQueue.clear(); } };
注意:重置前必须调用task->wait(),确保已取消的任务完全执行完毕,避免资源泄漏。
2. 手动销毁重建对象(C++11及以上)
利用std::destroy_at和std::construct_at直接在原内存位置销毁并重建对象,不需要修改成员类型:
void cancel_load() { task.cancel(); task.wait(); // 等待任务终止 // 销毁并重建task std::destroy_at(&task); std::construct_at(&task); // 销毁并重建arena std::destroy_at(&arena); std::construct_at(&arena); clear_queue(); std::cout << "Data loading is cancelled. Arena and Task have been reset" << std::endl; }
这种方式不需要额外的指针开销,但要确保调用wait()后再销毁,否则可能导致未定义行为。
额外注意事项
- 调用
task.cancel()后,任务不会立即停止,必须调用task.wait()等待所有任务彻底结束,再进行重置操作。 - 重置前一定要调用
clear_queue()清空队列,避免残留数据影响后续加载。
内容的提问来源于stack exchange,提问作者kyc12
相关产品推荐
相关产品推荐

