C++11中线程拷贝赋值被禁用,如何正确创建线程数组?
你碰到的这个问题核心在于:C++11里std::thread的拷贝构造和拷贝赋值运算符是被显式禁用的(标记为= delete)——毕竟线程是操作系统的资源,没法直接复制一个线程的运行状态。不过别担心,std::thread支持移动语义,我们可以通过几种方式来解决这个问题,同时还要注意线程的生命周期管理,不然程序可能会崩溃。
方案1:显式使用移动语义触发赋值
原代码里的赋值操作,理论上C++11及以后的编译器会自动把临时thread对象作为右值进行移动,但有些旧版本编译器可能需要显式指定std::move来触发移动赋值,避免编译器尝试调用被禁用的拷贝赋值:
修改循环内的代码:
threads[i] = std::move(std::thread(exec_produce, i + 1));
另外要注意:编译的时候必须开启C++11标准,比如GCC/Clang要加-std=c++11参数,MSVC要加/std:c++11,否则编译器可能不支持移动语义。
方案2:直接构造线程对象,避免默认构造后赋值
更优雅的方式是跳过默认构造空线程的步骤,直接在容器里构造线程。推荐用std::vector<std::thread>代替数组,它的emplace_back方法可以直接在容器内存里构造线程对象,完全不需要移动或赋值:
#include <vector> // 需要包含vector头文件 int main(int argc, const char *argv[]) { std::vector<std::thread> threads; threads.reserve(5); // 预分配5个元素的空间,避免后续扩容开销 cout << "create 5 threads ..." << endl; for (int i = 0; i < 5; i++) { threads.emplace_back(exec_produce, i + 1); // 直接在vector中构造线程 } cout << "finished creating threads ..." << endl; // 必须等待所有线程执行完成!否则main退出时会终止所有线程 for (auto& t : threads) { if (t.joinable()) { // 先检查线程是否可join t.join(); } } }
方案3:用初始化列表直接构造线程数组(适合参数固定的场景)
如果你的线程参数是固定的,也可以直接用初始化列表构造线程数组,一步到位:
int main(int argc, const char *argv[]) { std::thread threads[] = { std::thread(exec_produce, 1), std::thread(exec_produce, 2), std::thread(exec_produce, 3), std::thread(exec_produce, 4), std::thread(exec_produce, 5) }; cout << "finished creating threads ..." << endl; // 同样要等待所有线程完成 for (auto& t : threads) { if (t.joinable()) { t.join(); } } }
关键注意事项
不管用哪种方案,一定要在main函数退出前处理所有线程:要么调用join()等待线程执行完毕,要么调用detach()让线程在后台独立运行(但 detach 后就没法再控制线程了,要谨慎使用)。如果不做处理,当main函数退出时,std::thread的析构函数会调用std::terminate()直接终止程序,这属于未定义行为。
内容的提问来源于stack exchange,提问作者zhougb3

