C# 仅锁定集合中的对象而不是锁定整个集合是否安全?

C# 仅锁定集合中的对象而不是锁定整个集合是否安全?,c#,multithreading,C#,Multithreading,我想知道,假设我将此作为类实例级变量: Dictionary<int,Person> data = new Dictionary<int,Person>(); 然后在另一个线程中: foreach(KeyValuePair<int,Person> kvp in data) { // Enumerating to access the object's value as for read. console.WriteLine(kvp.Name);

我想知道,假设我将此作为类实例级变量:

Dictionary<int,Person> data = new Dictionary<int,Person>();
然后在另一个线程中:

foreach(KeyValuePair<int,Person> kvp in data)
{
   // Enumerating to access the object's value as for read.
   console.WriteLine(kvp.Name);
}
foreach(数据中的KeyValuePair kvp)
{
//枚举以访问对象的读取值。
console.WriteLine(kvp.Name);
}

从线程安全的角度来看,是否应该有任何问题?

只要没有任何东西在修改字典,它就是线程安全的,因此对于您的特定示例,不需要锁定,因为您没有修改字典本身

但是,您正在修改字典中的项目。但对于您的特定示例,锁定并没有实现任何功能,因为引用赋值(发生在执行
p.Name=“Something”
)是一个原子操作(假设
Name
实际上不是一个执行简单引用赋值以外的操作的属性)

因此,锁定不会改变您发布的代码中的任何行为

如果要对对象中的多个字段进行非原子更改(如赋值给double)或更改,则应在这些更改和访问受影响字段的任何内容周围引入锁

例如,如果您有一个具有
int
属性a和B的类,该类只能作为原子操作进行更改,则您必须在更改或访问字段时编写如下代码:

// Change fields.

lock (locker)
{
    A = newValueForA;
    B = newValueForB;
}

// Access fields.

int safeCopyOfA;
int safeCopyOfB;

lock (locker)
{
    safeCopyOfA = A;
    safeCopyOfB = B;
}

// Use safeCopyOfA and safeCopyOfB
实际上,您通常将这些字段包装在一个不可变的类中,并将其作为单个属性而不是两个相互依赖的属性公开,以简化代码并使其更加健壮

另外请注意,如果您正在更改字典本身(例如添加或删除项目),则应在字典的所有读写访问权限周围设置锁(始终使用相同的锁定对象)


或者使用

只要没有任何东西在修改字典,字典是线程安全的,因此对于您的特定示例,不需要锁定,因为您没有修改字典本身

但是,您正在修改字典中的项目。但对于您的特定示例,锁定并没有实现任何功能,因为引用赋值(发生在执行
p.Name=“Something”
)是一个原子操作(假设
Name
实际上不是一个执行简单引用赋值以外的操作的属性)

因此,锁定不会改变您发布的代码中的任何行为

如果要对对象中的多个字段进行非原子更改(如赋值给double)或更改,则应在这些更改和访问受影响字段的任何内容周围引入锁

例如,如果您有一个具有
int
属性a和B的类,该类只能作为原子操作进行更改,则您必须在更改或访问字段时编写如下代码:

// Change fields.

lock (locker)
{
    A = newValueForA;
    B = newValueForB;
}

// Access fields.

int safeCopyOfA;
int safeCopyOfB;

lock (locker)
{
    safeCopyOfA = A;
    safeCopyOfB = B;
}

// Use safeCopyOfA and safeCopyOfB
实际上,您通常将这些字段包装在一个不可变的类中,并将其作为单个属性而不是两个相互依赖的属性公开,以简化代码并使其更加健壮

另外请注意,如果您正在更改字典本身(例如添加或删除项目),则应在字典的所有读写访问权限周围设置锁(始终使用相同的锁定对象)


或者在您的案例中使用两个对象:集合实例和项目实例

您现在正在同步对集合的访问。只有在修改集合(添加、删除、清除、
new
)的情况下才能访问
数据的任何地方都应执行此操作。如果集合未更改,则不必同步对它的访问

项实例也可能需要同步(如果多个线程可以获取并使用它进行操作),同步逻辑与集合之前完全相同


与项目属性等相同。始终尝试思考谁将访问它以及如何访问它。开始使用并发集合可能是值得的,因为当线程数量增加时,
lock
可能会变得昂贵。访问其项目或修改集合不需要锁,但如果使用
new
,则可能必须使用
lock
来访问其实例(考虑使用
readonly
来保存其引用的字段)。

在您的示例中,有两个对象:集合实例和项目实例

您现在正在同步对集合的访问。只有在修改集合(添加、删除、清除、
new
)的情况下才能访问
数据的任何地方都应执行此操作。如果集合未更改,则不必同步对它的访问

项实例也可能需要同步(如果多个线程可以获取并使用它进行操作),同步逻辑与集合之前完全相同


与项目属性等相同。始终尝试思考谁将访问它以及如何访问它。开始使用并发集合可能是值得的,因为当线程数量增加时,
lock
可能会变得昂贵。访问其项目或修改集合不需要锁,但如果使用
new
,则可能必须使用
lock
来访问其实例(考虑使用
readonly
来保存其引用的字段)。

lockObj
不是集合中的对象,是吗?(这并不重要,但你在标题中提到了这一点)你为什么不使用a?你必须提供“安全”的定义。@LasseV.Karlsen-原子数据操纵,无竞争条件。如果我没有看到任何其他东西,请分享,这可能会有所帮助…@Thilo-是的,ockObj不是集合中的对象。
lockObj
不是集合中的对象,是吗?(这并不重要,但你在标题中提到了这一点)为什么不使用a?你必须提供“安全”的定义