代码如下,要问的问题写在代码注释里了..懂的朋友请告诉我一下,谢谢!
1 using System; 2 using System.Threading; 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 string a = "test"; 8 string b = "test"; 9 b += "kkk"; 10 Thread t = new Thread(new ThreadStart(() => 11 { 12 lock (a) 13 { 14 Thread.Sleep(5000); 15 16 Console.WriteLine("Finished"); 17 }; 18 })); 19 t.Start(); 20 while (!t.IsAlive) ; 21 Console.WriteLine(t.IsAlive); 22 Console.WriteLine(a);//问题1:在线程t中将a锁了,为什么在t没有完成之前,主线程仍然能访问a?(线程t没有执行完成就不会解锁对吧?) 23 Console.WriteLine(b);//问题2:为什么说C#中的string是比较特殊的类型?有人说string a="test",string b="test";a和b指向的是同一个对象,那么我把b改变之后,为什么a的值不会改变? 24 t.Join(); 25 Console.Read(); 26 } 27 28 29 }
lock(a) { .... }
表示的是以 "a" 为标识的锁,因此只有在使用到以 "a" 为标识的锁的地方才会有互斥。
因此,对于你的问题1,下面的代码能演示互斥结果:
lock(a) { Console.WriteLine(a);}
.NET中使用了字符串池技术(VS编译器对C/C++也能启用字符串池)。
string a = "test";
string b = "test";
表示的是a,b两个变量的值都为 "test",并非同一个对象。
b+="kkk";
此代码表示将变量 b 的值追加 "kkk" 字符串,注意,是对 b 的值修改,而非修改"test"这个值。
请讲 string b = "test"; 替换为 string b = a; 后观察执行结果。
以C#4.0为准,回答你的问题1:lock并不是什么神秘的东西,它其实就是一个方法调用:
//Monitor.cs
public static void TryEnter(object obj, ref bool lockTaken);
之所以lock块中的代码能做到线程安全,是因为当某个线程TryEnter成功之后,其他线程再到达TryEnter这句调用时会被block,直到之前的线程退出lock块。整个过程是跟属性/字段的访问没有关系的。并不是lock就是给这个字段加了一个锁,不让访问了等等。
回答问题2:a和b确实指向了同一个对象,但是执行b+="kkk"时,是把b指向了另外一个对象,这显然不会改变a。换个普通的类就很容易看懂:
class Test { } Test a = new Test(); Test b = a; //a和b此时指向同一个对象 b = new Test(); //显然a不会改变
c#对于字符串操作,会返回一个新的字符串,b+kkk将返回新的字符串,是另一个对象地址了。你可以 object.ReferenceEquals 测试两个是否为同一实例。你说的第一个问题确实是字符串池的问题,他确实有可能刚开始应用同一个对象,你也可以用上面的那个方法测试,虽然是同一个引用,但是对字符串的任何操作都会返回新的字符串,记住这点就行。
1.使用 lock时 才会检查a是否锁定。
2. 字符串 驻留,比较特殊,a,b 指向同一个对象。字符串的操作,更特殊,跟值类型一样,重新分配内存。
如果a,b是其他引用类型,又指向同一个对象,那他们直接的操作就是同一个对象,会互相影响。
public void test(){ Thread t = new Thread(new ThreadStart(() => { m1("子线程"); })); t.Start(); m1("主线程"); } object a = new object(); private void m1(string s) { lock (a)//去掉lock你发现输出的时间是一样的,所以lock只是锁住里面的代码块,不是锁住a,你上面的代码两个线程并不是同时执行同一个代码块 { Console.WriteLine(s + DateTime.Now.ToString()); Thread.Sleep(1000); } }
string a="test",string b="test"
在内存中它们是存在同一块内存的数据(整个程序范围),没必要存两份,如果b的值能改变的话,显然会影响到其它所有有相同值的字符串
,所以为什么说string是不可变的,所以你改变的不是b的值,而是将b指向了新生成的一个字符串,而a还是指向原来那个字符串的引用。
字符串驻留,你看看这方面的,当两个字符串的值一样,就指向同一个地址,就算是同一个string 如果你更改了这个对象的值, 就会另外的给这个对象分配地址。string++就是生成新的对象了。所以说对于大量的字符串拼接,生成很多string对象。对于lock 是在这个线程获取到这对象后,其他线程不能更改lock的对象,你这里只是
Console.WriteLine(a),并没有对a更改。