为什么锁定(“myLock”)会导致问题?

时间:2017-05-17 08:23:41

标签: c# parallel-processing locking parallel.foreach

我在Microsoft的C#Reference中已经阅读了以下内容:

  

lock(“myLock”)是一个问题,因为该过程中的任何其他代码   使用相同的字符串,将共享相同的锁。

究竟是什么意思?

以下代码是否无法正常工作? (我希望ReadyCount是一致的)

public class Calculator
{
    public int ReadyCount { get; private set; }

    public void IncreaseReadyCount()
    {
        lock ("ReadyCount")
        {
            ReadyCount++;
        }
    }

    public void Calculate()
    {
        Parallel.ForEach(list, litItem =>
        {
            IncreaseReadyCount();
        });
    }
}

2 个答案:

答案 0 :(得分:4)

问题是:该锁的范围是什么? 你无法告诉;它至少等同于<{1}}类型的静态/全局,但它也可能由任何其他代码偶然共享碰巧做了Calculator(给出了实习版本)和ldstr 'ReadyCount'(或使用lock等)。不太可能,但风险很大。

更重要的是,对于休闲读者而言并不是显而易见的,这是一个问题。如果你想要它是一个静态/全局锁,那么这相当于你的代码,但更明显,没有不相关的代码冒险锁定的风险:

Monitor

有了这个,至少它显然正在做什么。

就个人而言,我很想使用static readonly object readyCountLock = new object(); ... lock(readyCountLock) {...} :)

答案 1 :(得分:3)

除了Marc所说的,在现实生活中,人们会尝试锁定可能未被实现的字符串,例如从数据库记录中锁定某些键。如果您锁定实习字符串,则仅锁定字符串(种类)。但请考虑一下:

// not interned, but both strings represent "test"
string lock1 = new string(new char[] { 't', 'e', 's', 't' });
string lock2 = new string(new char[] { 't', 'e', 's', 't' });
Task.Run(() =>
{
    lock (lock1) {
        Console.WriteLine("1 entered");     
        Thread.Sleep(1000);
    }
});
Task.Run(() =>
{
    lock (lock2)
    {
        Console.WriteLine("2 entered");
        Thread.Sleep(1000);
    }
});

此代码立即执行两个“受保护”部分,因为尽管两个字符串都是“测试” - 但它们是不同的实例。所以锁定常量字符串是不可靠的,因为它是全局的,你永远不知道哪些代码使用了这样的“锁定”,并且锁定字符串变量是危险的,因为它可能根本不起作用。

回答关于锁定ReadyCount.ToString()的评论。这正是人们在现实生活中尝试这样做的方式(其中ReadyCount是数据库记录的某些属性或类似)。我通过ReadyCount 你的意思是一些数字,而不是真正的字符串(否则调用ToString毫无意义)。不,这也很糟糕,因为:

int readyCount = 1;
string lock1 = readyCount.ToString();
string lock2 = readyCount.ToString();
bool same = Object.ReferenceEquals(lock1, lock2);
// nope, not the same, lock will not work