4

今天早上我刚刚对锁定多线程进行了一些随机测试,奇怪的是我发现在两个单独的实例中锁定一个私有“字符串”实际上会阻止另一个线程执行。请在下面找到代码以供参考。

让我感到困惑的是,两个对象中的“字符串”实际上是两个独立的对象,那么为什么锁定一个会阻塞另一个呢?(注意,如果将字符串替换为其他引用类型的对象,如 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);
        }
    }
}
4

3 回答 3

6

字符串常量是“interned”。这意味着当您键入:

var s1 = "foo";
var s2 = "foo";

两者都是字符串“foo”的同一个实例。同样,当您从不同的线程两次调用具有类似定义的局部变量的方法时,它是同一个实例。这样做是出于性能原因。

这是一种特殊情况,但另一方面,您确实不应该锁定字符串。(我还没有看到创建新锁对象的惯用解决方案不是可行的方法—— private object lockObject = new object();

于 2012-06-22T01:50:16.640 回答
2

.NET 中的文字字符串是interned,因此每次使用相同的文字字符串时,实际上都是在使用完全相同的对象。因此,您引用的两个线程"test"都在引用(并锁定)同一个对象。

创建一个新List类型或其他类型会为您提供一个新对象,因此每个线程都会锁定自己的对象。

于 2012-06-22T01:50:55.347 回答
2

当您使用字符串时它会阻塞,因为您的所有Test类都使用相同的字符串实例。C# 将实习所有字符串文字以节省内存。所以这只是字符串“test”的一次实例。

公共语言运行时通过维护一个称为实习池的表来保存字符串存储,该表包含对程序中以编程方式声明或创建的每个唯一文字字符串的单个引用。因此,具有特定值的文字字符串的实例在系统中仅存在一次。

所以所有的实例Test都使用同一个对象来锁定。

于 2012-06-22T01:51:19.870 回答