首页 新闻 搜索 专区 学院

C# 关于lock关键字和string类型的疑问...请指教...

0
悬赏园豆:5 [已解决问题] 解决于 2012-09-13 16:12

代码如下,要问的问题写在代码注释里了..懂的朋友请告诉我一下,谢谢!

 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 }
hexllo的主页 hexllo | 菜鸟二级 | 园豆:405
提问于:2012-09-12 10:28
< >
分享
最佳答案
-1

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; 后观察执行结果。

 

收获园豆:2
Launcher | 高人七级 |园豆:45045 | 2012-09-12 10:47
其他回答(5)
1

以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不会改变
收获园豆:1
水牛刀刀 | 园豆:6350 (大侠五级) | 2012-09-12 10:53
0

 c#对于字符串操作,会返回一个新的字符串,b+kkk将返回新的字符串,是另一个对象地址了。你可以 object.ReferenceEquals 测试两个是否为同一实例。你说的第一个问题确实是字符串池的问题,他确实有可能刚开始应用同一个对象,你也可以用上面的那个方法测试,虽然是同一个引用,但是对字符串的任何操作都会返回新的字符串,记住这点就行。

收获园豆:1
程序员的人生 | 园豆:235 (菜鸟二级) | 2012-09-12 11:12
0

1.使用 lock时 才会检查a是否锁定。

2.  字符串 驻留,比较特殊,a,b 指向同一个对象。字符串的操作,更特殊,跟值类型一样,重新分配内存。

    如果a,b是其他引用类型,又指向同一个对象,那他们直接的操作就是同一个对象,会互相影响。

Qlin | 园豆:2403 (老鸟四级) | 2012-09-12 11:12
0

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还是指向原来那个字符串的引用。
收获园豆:1
向往-SONG | 园豆:4853 (老鸟四级) | 2012-09-12 11:30
0

字符串驻留,你看看这方面的,当两个字符串的值一样,就指向同一个地址,就算是同一个string 如果你更改了这个对象的值, 就会另外的给这个对象分配地址。string++就是生成新的对象了。所以说对于大量的字符串拼接,生成很多string对象。对于lock 是在这个线程获取到这对象后,其他线程不能更改lock的对象,你这里只是

 Console.WriteLine(a),并没有对a更改。
Rookier | 园豆:652 (小虾三级) | 2012-09-13 11:51
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册