用于值存储的多线程锁定语句 类
Multithreading lock statement for value storing classes
虽然我已经通过文档和论坛阅读了很多内容,但我仍然不清楚数据缓存和线程间共享数据的可见性。
我很清楚如何使用 lock 语句更新不同线程共享的值。
举个例子:
public class Foo
{
object locker = new object();
int someValue;
public void SetValue (int value)
{
lock (locker)
{
someValue = value;
}
}
public int GetValue()
{
int value ;
lock(locker)
{
value = someValue;
}
return value;
}
}
但现在让我们更深入地了解我的问题所在。
考虑一个名为 Person
的 class,它是预定义的且无法修改(例如,它来自另一个程序集)
一个class实例应该在两个线程之间传递。
public class Person
{
public string Name { get; }
public string SirName { get; }
public uint Age { get; }
public bool EntryValid { get; }
public Person(string name, string sirName, uint age)
{
Name = name;
SirName = sirName;
Age = age;
EntryValid = true;
}
public Person()
{
EntryValid = false;
}
}
public class FoundResult
{
private object locker;
private Person person;
private bool result;
public FoundResult ()
{
locker = new object();
// Is lock required here?
result = false;
}
// Called from Thread No 1
public void PlaceResult(Person person)
{
lock (locker)
{
this.person = person; // This may be extended by an additional Clone();
result = true;
}
}
// Called from Thread No 2
public bool HasResult()
{
bool result;
lock (locker)
{
result = this.result;
}
return result;
}
// Called from Thread No 2
public Person GetResult()
{
Person person;
lock (locker)
{
person = this.person; // This may be extended by an additional Clone();
result = false;
}
return person;
}
}
为了减少示例的重载,我假设线程将被正确地发送信号,以便它们知道何时访问这些方法。然后它会像这样::
线程 1 将被要求搜索一个人(例如通过保存在线程 space 中的列表)并通过调用 PlaceResult
放置结果。该线程将向线程号发送信号。 2 将调用 GetResult()
.
现在的问题是 person
实例中存储的数据的可见性。 lock 是否保证 person
中的值(姓名、年龄等)从线程 1 传递到线程 2?
或者是否仍然有可能两个线程只在它们的缓存或寄存器上工作,而线程 2 有时读取相同的“旧”数据?
如果是这样,我如何在不触及 Person
class 本身的情况下正确传递数据?
Does lock guarantee that the values (name, age etc.) in 'person' are passed from thread no 1 to no 2.
是的。 Lock 在进入和离开时都意味着一个完整的内存屏障。
Or is it still possible that both threads only work on their caches or registers and thread no 2 reads sometimes the same "old" data.
没有。由于存在内存屏障,两个线程都将被迫从内存中读取实际值。
锁的伟大之处在于它们相当安全。只要共享字段只能从锁内部访问,它就应该是安全的。使用 Interlocked 或 volatile 可能会在您的示例中获得正确的行为。但如果它不是性能关键,你也可以使用锁来保证安全。
// Is lock required here?
result = false;
不是,这是在构造函数中调用的。在构造函数内部,对象只能由创建对象的线程访问。至少只要构造函数不为另一个线程提供对自身的引用。据我所知,任何其他访问该对象的线程都需要至少从内存中加载一次。这涉及我不确定的技术细节,但我希望 GC 在回收内存时至少发出内存屏障,从而确保其他线程在重用内存地址时无法观察到回收的对象。
虽然我已经通过文档和论坛阅读了很多内容,但我仍然不清楚数据缓存和线程间共享数据的可见性。
我很清楚如何使用 lock 语句更新不同线程共享的值。
举个例子:
public class Foo
{
object locker = new object();
int someValue;
public void SetValue (int value)
{
lock (locker)
{
someValue = value;
}
}
public int GetValue()
{
int value ;
lock(locker)
{
value = someValue;
}
return value;
}
}
但现在让我们更深入地了解我的问题所在。
考虑一个名为 Person
的 class,它是预定义的且无法修改(例如,它来自另一个程序集)
一个class实例应该在两个线程之间传递。
public class Person
{
public string Name { get; }
public string SirName { get; }
public uint Age { get; }
public bool EntryValid { get; }
public Person(string name, string sirName, uint age)
{
Name = name;
SirName = sirName;
Age = age;
EntryValid = true;
}
public Person()
{
EntryValid = false;
}
}
public class FoundResult
{
private object locker;
private Person person;
private bool result;
public FoundResult ()
{
locker = new object();
// Is lock required here?
result = false;
}
// Called from Thread No 1
public void PlaceResult(Person person)
{
lock (locker)
{
this.person = person; // This may be extended by an additional Clone();
result = true;
}
}
// Called from Thread No 2
public bool HasResult()
{
bool result;
lock (locker)
{
result = this.result;
}
return result;
}
// Called from Thread No 2
public Person GetResult()
{
Person person;
lock (locker)
{
person = this.person; // This may be extended by an additional Clone();
result = false;
}
return person;
}
}
为了减少示例的重载,我假设线程将被正确地发送信号,以便它们知道何时访问这些方法。然后它会像这样::
线程 1 将被要求搜索一个人(例如通过保存在线程 space 中的列表)并通过调用 PlaceResult
放置结果。该线程将向线程号发送信号。 2 将调用 GetResult()
.
现在的问题是 person
实例中存储的数据的可见性。 lock 是否保证 person
中的值(姓名、年龄等)从线程 1 传递到线程 2?
或者是否仍然有可能两个线程只在它们的缓存或寄存器上工作,而线程 2 有时读取相同的“旧”数据?
如果是这样,我如何在不触及 Person
class 本身的情况下正确传递数据?
Does lock guarantee that the values (name, age etc.) in 'person' are passed from thread no 1 to no 2.
是的。 Lock 在进入和离开时都意味着一个完整的内存屏障。
Or is it still possible that both threads only work on their caches or registers and thread no 2 reads sometimes the same "old" data.
没有。由于存在内存屏障,两个线程都将被迫从内存中读取实际值。
锁的伟大之处在于它们相当安全。只要共享字段只能从锁内部访问,它就应该是安全的。使用 Interlocked 或 volatile 可能会在您的示例中获得正确的行为。但如果它不是性能关键,你也可以使用锁来保证安全。
// Is lock required here? result = false;
不是,这是在构造函数中调用的。在构造函数内部,对象只能由创建对象的线程访问。至少只要构造函数不为另一个线程提供对自身的引用。据我所知,任何其他访问该对象的线程都需要至少从内存中加载一次。这涉及我不确定的技术细节,但我希望 GC 在回收内存时至少发出内存屏障,从而确保其他线程在重用内存地址时无法观察到回收的对象。