C# 是否需要在 getter 和 setter 中锁定?

Dan*_*şar 5 c# multithreading locking properties volatile

当多个线程将通过 get/set 函数访问属性/字段时,是否有必要锁定 getter 和 setter。

例如:

您有一个计时器,它可以定期获取对象的值。以及定期更新此值的过程。

public class Task : IProgressProvider
{
    ...
    public Progress GetProgress()
    {
        // should i lock here?
        return _progress;
    }

    public SetProgress(Progress progress)
    {
        // and lock here?
        _progress = progress;
    }

    public void Execute()
    {
        // ...
        SetProgress(new Progress(10));
        // ...
        SetProgress(new Progress(50));
        // ...
        SetProgress(new Progress(100));
    }
}

public class ProgressReporter
{
    public ProgressReporter()
    {
        _timer = new Timer();
        _timer.Elapsed += Elapsed;
        // ...
    }

    // ...

    public void Elapsed(...)
    {
        var progress = _task.GetProgress();
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

问题又来了:

  • 我应该锁定 GetProgress 和 SetProgress 函数吗?
  • 如果是,为什么?
  • 在这种情况下是否需要 volatile(假设有一个或多个处理器内核)
  • 为了保持“简单”,让我们假设 Progress 中的属性/字段是只读的。编辑:我知道 += 等不是原子操作,需要正确处理(fe 锁定)

我认为设置和读取变量 _progress 是一个原子操作。GetProgress 将获得非常当前的值并不重要。如果这次没有,它将在下一次调用 GetProgress 时获取它。

Mat*_*son 4

如果_progress是引用类型,那么读取和写入其值(即更改引用)确实是一个原子操作,因此您不需要锁定示例中的特定代码。但是,如果您在 setter 或 getter 中更改了多个字段,或者该字段不是具有原子读/写的类型(例如 double),那么您需要锁定。

如果您希望多个线程在任何时刻观察到相同的值,那么您确实需要额外的锁定。如果您不关心一个线程是否可以读取一个值而另一个线程是否可以读取另一个值(因为它在中间发生了变化),那么锁定似乎并不真正有必要。

我肯定会让它变得不稳定。volatile正是为了这个目的。它至少会阻止优化编译器在不应该缓存值的情况下缓存该值(以防它会做这样的事情)。

总结一下:对于您所需的用途,您应该使其成为_progress不稳定的,但不需要锁定对其的访问。