这段代码安全吗,是否可以从构造函数C++生成线程
is this code safe , is it ok to spawn a thread from a constructor C++?
我需要在C++类中嵌入一个线程,这是一种活动对象,但不完全是。 我正在从类的构造函数中生成线程,可以这样做吗 这种方法有任何问题吗?
#include <iostream>
#include <thread>
#include <unistd.h>
class xfer
{
int i;
std::shared_ptr<std::thread> thr;
struct runnable {
friend class xfer;
void operator()(xfer *x) {
std::cerr<<"thread started, xfer.i:"<<x->i;
}
} run;
public:
xfer() try : i(100), thr(new std::thread(std::bind(run, this))) { } catch(...) { }
~xfer() { thr->join(); }
};
int
main(int ac, char **av)
{
xfer x1;
return 0;
}
通常,在构造函数中启动线程没有问题,前提是线程中使用的对象已完全构造在开始之前。 (因此,例如,具有线程的成语基类,在其构造函数中启动线程本身,是坏了。 在你的情况下,你没有遇到这个标准,因为线程对象使用您的成员run
,直到启动线程后。 将线程的创建移动到构造函数的主体,或者只是更改数据的顺序类定义中的成员将更正此问题。 (如果您执行后者,请添加注释,大意是顺序很重要,以及为什么。
在析构函数中调用join
问题更多。 任何操作恕我直言,这可能会等待不确定的时间是有问题的析构函数。 在堆栈展开期间调用析构函数时,您不想坐在那里等待另一个线程完成。
此外,您可能希望使类不可复制。 (在这种情况下,你不需要shared_ptr
。 如果你复制,你最终会做 在同一线程上join
两次。
您的代码具有争用条件,不安全。
问题是成员变量的初始化按照它们在类中声明的顺序进行,这意味着成员thr
在成员run
之前初始化。在初始化thr
期间,您将在run
上调用std::bind
,这将在内部复制(尚未初始化的)run
对象(您知道您正在那里复制吗?
假设您传递了一个指向run
的指针(或用于std::ref
std::bind
以避免复制),代码仍将具有争用条件。在这种情况下,将指针/引用传递给std::bind
不会成为问题,因为只要不访问指针/引用,就可以将指针/引用传递给尚未初始化的成员。但是,仍然存在争用条件,即 std::thread
对象可能生成线程的速度太快(例如,运行构造函数的线程被逐出,新线程处理),并且最终可能导致线程在初始化run
对象之前执行run.operator()
。
您需要对类型中的字段重新排序以使代码安全。既然您知道成员顺序的微小更改可能会对代码的有效性产生干扰影响,那么您也可以考虑更安全的设计。(在这一点上,这将是正确的,它实际上可能是你需要的,但至少注释类定义,以便以后没有人对字段重新排序)
在构造函数中使用可能是安全的,但是...
- 应避免在函数调用中使用裸
new
- 在析构函数中使用
thr->join()
是...指示问题
在细节...
不要在函数调用中使用裸new
由于未指定操作数的执行顺序,因此它不是异常安全的。更喜欢在这里使用 std::make_shared
,或者在 unique_ptr
的情况下,创建您自己的make_unique
工具(使用完美的转发和可变参数模板,它就可以工作);这些构建器方法将确保 RAII 类的所有权分配和归属不可分割地执行。
知道三法则
三法则是经验法则(为 C++03 建立),它说如果你需要编写一个复制构造函数、赋值运算符或析构函数,你可能也应该写另外两个。
在您的情况下,生成的赋值运算符不正确。也就是说,如果我创建了两个对象:
int main() {
xfer first; // launches T1
xfer second; // launches T2
first = second;
}
然后在执行作业时,我丢失了对T1
的引用,但我从未加入过它!!
我不记得join
调用是否是强制性的,但是您的类根本不一致。如果不是必需的,请删除析构函数;如果它是必需的,那么你应该编写一个只处理一个共享线程的较低级别的类,然后确保共享线程的销毁始终在join
调用之前,最后直接在xfer
类中使用该共享线程工具。
根据经验,可能应该拆分对其元素子集具有特殊复制/分配需求的类,以将具有特殊需求的元素隔离在一个(或几个)专用类中。
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 具有默认模板类型的默认构造函数的类型推导
- 使用dynamic_cast和构造函数时出错
- 在c++中使用向量时,如何调用构造函数和析构函数
- 奇怪的构造函数行为