我还是不清楚什么时候应该在一些代码周围包装一个锁。我的一般经验法则是在读取或写入静态变量时将操作包装在锁中。但是,当仅读取静态变量时(例如,它是在类型初始化期间设置的只读),则不需要将其包装在lock语句中,对吗?我最近看到了一些类似于以下示例的代码,这使我认为我的多线程知识可能存在一些不足:

class Foo
{
    private static readonly string bar = "O_o";

    private bool TrySomething()
    {
        string bar;

        lock(Foo.objectToLockOn)
        {
            bar = Foo.bar;
        }

        // Do something with bar
    }
}

但这对我来说毫无意义-为什么读寄存器会出现并发问题?

同样,此示例提出了另一个问题。其中之一比另一个更好吗? (例如,示例二持有锁的时间更少了?)我想我可以拆卸MSIL了……
class Foo
{
    private static string joke = "yo momma";

    private string GetJoke()
    {
        lock(Foo.objectToLockOn)
        {
            return Foo.joke;
        }
    }
}


class Foo
{
    private static string joke = "yo momma";

        private string GetJoke()
        {
            string joke;

            lock(Foo.objectToLockOn)
            {
                joke = Foo.joke;
            }

            return joke;
        }
}

最佳答案

由于您编写的代码均未在初始化后修改静态字段,因此无需进行任何锁定。仅用新值替换字符串也不需要同步,除非新值取决于读取旧值的结果。
静态字段不是唯一需要同步的事物,任何可以修改的共享引用都容易受到同步问题的影响。

class Foo
{
    private int count = 0;
    public void TrySomething()
    {
        count++;
    }
}
您可能假设执行TrySomething方法的两个线程就可以了。但事实并非如此。
  • 线程A将计数(0)的值读入寄存器,因此可以递增。
  • 上下文切换!线程调度程序确定线程A有足够的执行时间。接下来的是线程B。
  • 线程B将计数(0)的值读入寄存器。
  • 线程B递增寄存器。
  • 线程B保存结果(1)进行计数。
  • 上下文切换回A.
  • 线程A将保存在其堆栈中的count(0)值重新加载到寄存器中。
  • 线程A递增寄存器。
  • 线程A保存结果(1)进行计数。

  • 因此,即使我们两次调用count++,count的值也才刚刚从0变为1。让我们使代码成为线程安全的:
    class Foo
    {
        private int count = 0;
        private readonly object sync = new object();
        public void TrySomething()
        {
            lock(sync)
                count++;
        }
    }
    
    现在,当线程A被中断时,线程B不会弄乱计数,因为它会打到lock语句,然后阻塞直到线程A释放同步。
    顺便说一句,还有另一种方法可以使递增的Int32和Int64成为线程安全的:
    class Foo
    {
        private int count = 0;
        public void TrySomething()
        {
            System.Threading.Interlocked.Increment(ref count);
        }
    }
    
    关于您的问题的第二部分,我想我会选择比较容易理解的那个,任何性能差异都可以忽略不计。早期的优化是万恶之源,等等。
    Why threading is hard

    关于c# - 锁定C#,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/105198/

    10-15 14:38