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?你必须提供“安全”的定义