这些惰性内联字符串锁实际上是如何工作的,它们有效吗,它们会引起问题吗?

How does these lazy inline string-locks actually work, do they work, do they cause problem?

所以,我一直在使用字符串作为 "uniquely identifiable mutexes",但我不确定它们是否具有我希望它们具有的效果,现在我遇到了问题

我在这里的一个多线程应用程序中工作,我的锁遇到了奇怪的行为,我想确保它是 "Dumb locking" 而不是 "using dumb locks"

我的锁是这样的

private Stuff _stuff = null;
public Stuff Stuff {
    get {
        lock($"{this.SomeIdentifier}_stuff_initialization") {
            if(stuff == null) {
                stuff = new Stuff();
                stuff.DoLazyInitialization(this.SomeOtherLazyInitedStuff);
                return stuff;
            } else {
                return stuff;
            }
        }
    }
}

太垃圾了?作品?写这类代码需要注意什么?

使用字符串作为互斥体进行锁定时存在问题,这里有一个很好的答案:

Using string as a lock to do thread synchronization

除此之外,您的逻辑可能有问题。如果 'SomeIdentifier' 是可变的,那么你 运行 进入可能的竞争条件,其中:

线程 A 在对象实例 1

上调用 get

线程 B 在对象实例 1

上更新 SomeIdentifer

线程 A 在对象实例 1

上锁定并实例化 stuff

线程 B 在对象实例 1 上调用 get(锁定在不同的字符串上)

线程 A 调用 DoLazyInitialization

线程 B returns 'stuff'(因为它不为空)在 DoLazyInitialization 完成之前。

我发现(困难的方法)实际上用 $ string 锁定是非常糟糕的,它不起作用。

这是我用来测试的代码:

object o = new object();
Parallel.For(0, 1000, (i) => {
    lock (o) {
        Console.WriteLine($"Hey {i}");
        Thread.Sleep(5000);
    }
});

它看起来很愚蠢,因为它是一个愚蠢的测试,并且会发生预期的情况:它正确地锁定了区域。

下面的也行

var lid = "SomeId";
Parallel.For(0, 1000, (i) => {
    lock(lid) {
        Console.WriteLine($"Hey {i}");
        Thread.Sleep(5000);
    }
});

Parallel.For(0, 1000, (i) => {
    lock("SomeId") {
        Console.WriteLine($"Hey {i}");
        Thread.Sleep(5000);
    }
});

现在,如果您尝试动态组合字符串并将其用作锁定互斥锁,它将无法正确锁定,它将生成同一字符串的许多不同实例,您将获得多个不同的 "locks"允许不同的线程访问资源。

这不行

var lid = "SomeId";
Parallel.For(0, 1000, (i) => {
    lock($"{lid}_LOCKING") {
        Console.WriteLine($"Hey {i}");
        Thread.Sleep(5000);
    }
});

var lid = "SomeId";
Parallel.For(0, 1000, (i) => {
    lock(lid + "_LOCKING") {
        Console.WriteLine($"Hey {i}");
        Thread.Sleep(5000);
    }
});

下面的代码有效,但如果动态表达式可能生成太多唯一字符串,我会避免:

var 盖子 = "SomeId"; Parallel.For(0, 1000, (i) => { 锁(String.Intern($“{lid}_LOCKING”)){ Console.WriteLine($"Hey {i}"); Thread.Sleep(5000); } });

以前我可能懒得用这种方式测试,但现在我清楚地知道这是一种愚蠢的锁定方式。不要使用非常量字符串进行锁定,即使用于创建该字符串的字符串组件是不可变的,它也不起作用;