我刚刚在今天早上对多线程锁定进行了一些随机测试,奇怪的是我发现在两个单独的实例中锁定一个私有“字符串”实际上会阻止另一个线程执行。请在下面找到代码以供参考。
令我困惑的是两个对象中的“字符串”实际上是两个独立的对象,那么为什么锁定一个阻止另一个呢? (注意,如果用其他引用类型对象替换字符串,如List,它不会阻止其他线程执行,这实际上是我们所期望的......)
class Program {
static void Main(string[] args) {
Thread th = new Thread(DoWork);
th.Start();
Thread th2 = new Thread(DoWork);
th2.Start();
}
static void DoWork() {
Test importer = new Test();
importer.SyncTest();
}
}
public class Test {
public void SyncTest() {
string find = "test";
lock(find) {
Console.WriteLine("thread starting...");
Thread.Sleep(4000);
}
}
}
答案 0 :(得分:6)
字符串常量为"interned"。这意味着当您输入:
var s1 = "foo";
var s2 = "foo";
两者都是字符串“foo”的实例。同样,当您从不同的线程调用两次具有类似定义的局部变量的方法时,它是相同的实例。这是出于性能原因而完成的。
这是一种特殊情况,但另一方面,你真的不应该锁定字符串。 (我还没有看到创建一个新的锁定对象的身体解决方案不能实现的情况 - private object lockObject = new object();
)
答案 1 :(得分:2)
.NET中的文字字符串是interned,因此每次使用相同的文字字符串时,实际上都使用完全相同的对象。因此,引用"test"
的两个线程都引用(并锁定)同一个对象。
创建新的List
或其他类型会为您提供一个新对象,因此每个线程都会锁定自己的对象。
答案 2 :(得分:2)
它在您使用字符串时会阻塞,因为所有Test
类都使用相同的字符串实例。 C#将intern所有字符串文字以节省内存。所以这只是字符串“test”的一个实例。
公共语言运行库通过维护a来保存字符串存储 表,称为实习池,包含单个引用 以编程方式声明或创建的每个唯一文字字符串 你的计划。因此,带有一个文字字符串的实例 特殊值仅在系统中存在一次。
因此Test
的所有实例都使用相同的对象来锁定。