You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:24:15