我有一个相当简单的问题,但答案似乎让我难以捉摸。如果我有同一个 c++ 类的多个对象,每个对象在它自己的线程中,我是否需要注意并发访问问题?或者对于单独的实例,事情是否自动线程安全?当然,我希望静态方法会出现问题,但是实例方法呢?
6 回答
如果您在每个线程中有单独的对象,那么您会没事的。但是,如果您在该类中有一个静态成员变量,您可能会遇到问题。
显然,这仅适用于所讨论的类对象的数据,如果您的线程函数正在访问全局或共享数据,那么通常的多线程问题将适用。
这取决于。通常,一个类的一个实例将独立于对其他实例的操作。如果在单个线程中是这种情况,那么在多个线程中也是如此。
例如,考虑一个表示 Point 的值类型类。
class Point {
public:
int x, y, z
};
一个线程中此类的实例将不受另一个线程中不同实例上的操作的影响。
但是,类的实例可以与其他对象交互。如果两个实例可以与同一个对象交互,那么是的,您确实需要关注线程安全。
实例变量都是独立的。因此,如果您的实例方法仅使用实例和局部变量,则无需担心线程安全。
只要来自一个对象的线程不调用另一个对象的方法,那么应该没有问题。
但是,如果您有递归方法并且正在获取方法中的互斥锁,您可能仍然会遇到死锁。如果是这种情况,您将需要使用递归互斥锁。
您问题的根源在于关于对象如何在内存中布局的问题。假设不涉及静态数据成员(很少见),每个对象都独立于其他相同类型的对象,因为在内存中布局的对象本身仅由对象的几个数据成员组成。例如,假设类型定义
class Location {
private:
double latitude1;
double longitude1;
public:
double latitude () const { return latitude1; }
double longitude() const { return longitude1; }
Location(const double lat0, const double long0) {
latitude1 = lat0;
longitude1 = lon0;
}
// Calculate the Location at exactly the furthest
// point on earth (implemented elsewhere).
Location antipode();
};
假设该类型的单独对象在别处实例化为
Location my_loc(-100.0, 35.0);
const Location your_loc(15.0, 45.5);
然后,my_loc
它本身只包含内存中的一对双精度数(在这种情况下,堆栈上的一对连续双精度数);并且your_loc
它本身只包含一对单独的双打。所以,你看,就数据而言,aLocation
只不过是两个双精度数的结构。
但是,你问:构造函数和antipode()
,等等呢?答案是它们每个类都只存在一次——而且,就内存而言,它们与数据没有直接关联。只有编译器才能将数据与函数关联起来。线程不关心这个。
如果您根据上述情况考虑到这一点,那么人们会怀疑您一直没有得到的答案将落在您身上。
您可以大致将 C++ 的项目分为 4 类:
- 只读:字面量、常量、函数等......在代码执行期间不能修改,因此共享本质上是安全的
- 全局变量:命名空间范围内的全局变量、类的静态属性、函数中的局部静态等……可以修改,因此必须使用适当的多线程感知机制
- 线程局部变量:以上所有,但标有
thread_local
存储限定符;看下一点 - 实例变量:类或结构属性,函数局部变量,......只要它们没有被显式共享,就可以安全使用
综上所述,您不必担心方法或函数,并且可以自由共享常量。
在中间范围内,确保不共享(即使是无意的)局部变量,至少在确保访问时正确同步的情况下不共享。
最后,要特别警惕全局变量。