用于值存储的多线程锁定语句 类

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 在回收内存时至少发出内存屏障,从而确保其他线程在重用内存地址时无法观察到回收的对象。