这些惰性内联字符串锁实际上是如何工作的,它们有效吗,它们会引起问题吗?
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);
}
});
以前我可能懒得用这种方式测试,但现在我清楚地知道这是一种愚蠢的锁定方式。不要使用非常量字符串进行锁定,即使用于创建该字符串的字符串组件是不可变的,它也不起作用;
所以,我一直在使用字符串作为 "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); } });
以前我可能懒得用这种方式测试,但现在我清楚地知道这是一种愚蠢的锁定方式。不要使用非常量字符串进行锁定,即使用于创建该字符串的字符串组件是不可变的,它也不起作用;