class TestWorker
 2 {        
 3     public void DoMultiThreadedWork(object someParameter)
 4     {
 5         lock (lockObject)
 6         {
 7             //  lots of work 
 8         }
 9     }
10 
11     private string lockObject = "lockit";
12 } 

这段代码很简单,大家看看这段代码有什么问题呢?

开始这么一看似乎没什么大的问题。可是仔细分析一下代码你就可以知道其中还是有一些很严重的问题的。假如你对Lock机制有所了解并且对string类型有过研究的话你就会发现出问题:

lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围。在上例中,锁的范围限定为此函数,因为函数外不存在任何对该对象的引用。严格地说,提供给 lock 的对象只是用来唯一地标识由多个线程共享的资源,所以它可以是任意类实例。然而,实际上,此对象通常表示需要进行线程同步的资源。例如,如果一个容器对象将被多个线程使用,则可以将该容器传递给 lock,而 lock 后面的同步代码块将访问该容器。只要其他线程在访问该容器前先锁定该容器,则对该对象的访问将是安全同步的。通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。关于Lock被编译器编译过后的代码请参考我的另一篇文章:Inside
C#

String在CLR中有两个重要的属性:不变性和字符串驻留。这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。因此,最好锁定不会被暂留的私有或受保护成员。

所以锁定字符串尤其危险。

下面我们来看看字符串驻留是个怎样的东东。看看下面的代码

 1              string a = "lockit";  
 2              string b = "lockit"; 
 3              string c = "LOCKIT".ToLower();  
 4              string d = "lock" + "it"; 
 5              string e1 = "lock";
 6              string e2 = "it";
 7              string ee = e1 + "it";
 8              string e = e1 + e2; 
 9              Console.WriteLine(object.ReferenceEquals(a, b));
10              Console.WriteLine(object.ReferenceEquals(a, c));
11              Console.WriteLine(object.ReferenceEquals(a, d));
12              Console.WriteLine(object.ReferenceEquals(a, ee));
13              Console.WriteLine(object.ReferenceEquals(a, e));

下边是输出的结果:

True,False,True,False,False

我们知道CLR处理每个对象在内存的时候都会额外的生成一个syncblockindex空间,这个就是用于对象同步用的。是大家平时开发使用最多的一个类型,MS的CLR部门为了简化操作和性能的优化做了两点处理,一是将string的创建过程简单化。一般的对象在创建的时候通过new关键字来实现;而string不需要这么做,我们只需要把对应的字符换赋给给对应的字符串变量就可以了。那么他们在创建过程中使用的MSIL指令时不同的——一般的引用对象的创建是通过newobj这样一个IL指令来实现的,而创建一个字符串变量的IL指令则是ldstr(load
string)。其次就是为了考虑性能的提升和内存节约上,对于创建相同的字符串,一般不会为他们分别分配内存块,相反地,他们会共享一块内存。CLR内部维护一个HashTable,这HashTable维护者大部分创建的string。这个HashTable的Key对应的相应的string本身,而Value则是分配给这个string的内存块的引用。我们知道在一个托管进程被创建以后,在托管进程的内存空间里面,包含了System
Domain,Shared Domain等等,而这个HashTable就是放在System
Domain里,所以它是在这整个程序的生命周期里都是存在的和被共享的。一般地,在程序运行过程中,如果需要的创建一个string,CLR会根据这个string的Hash
Code试着在HashTable中找这个相同的string,如果找到,则直接把找到的string的地址赋给相应的变量,如果没有则在托管堆中创建一个string,CLR会先在managed
heap中创建该strng,并在HashTable中创建一个Key-Value,Key为这个string本身,Value为string的内存地址,这个地址最重被赋给响应的变量。

上面的例子后面两个为False告诉我们对于对一个动态创建的字符串,驻留机制便不会起作用。这种情况产生的MSIL也不一样。但是我们可以用System.String中的静态方法Intern来解决。

公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个。

例如,如果将同一字符串分配给几个变量,运行库就会从拘留池中检索对该字符串的相同引用,并将它分配给各个变量。

Intern 方法使用拘留池来搜索与 str 值相等的字符串。如果存在这样的字符串,则返回拘留池中它的引用。如果不存在,则向拘留池添加对 str 的引用,然后返回该引用。

在下面的 C# 示例中,值为“MyTest”的字符串 s1 已经留用,因为它在程序中是一个字符串常量。

System.Text.StringBuilder 类生成与 s1 同值的新字符串对象。对该字符串的引用被分配给 s2。

Intern 方法搜索与 s2 具有相同值的字符串。由于存在这样的字符串,该方法会返回分配给 s1 的同一引用,然后将该引用分配给 s3。

引用 s1 和 s2 的比较结果是不相等的,这是因为它们引用的是不同的对象;而引用 s1 和 s3 的比较结果是相等的,因为它们引用的是相同的字符串。

 
 
 String s1 = "MyTest";
String s2 = new StringBuilder().Append("My").Append("Test").ToString();
String s3 = String.Intern(s2);
Console.WriteLine((Object)s2==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.

将此方法与 IsInterned 方法进行比较。

C#中字符串 "驻留"与Lock(转载)的更多相关文章

  1. C#中字符串驻留技术

    转自:http://www.cnblogs.com/Charles2008/archive/2009/04/12/1434115.html MSDN概念:公共语言运行库通过维护一个表来存放字符串,该表 ...

  2. Java中的字符串驻留

    转自:http://www.cdtarena.com/javapx/201307/9088.html 最近在工作的时候,一句再正常不过的代码String a = “hello” + “world”;被 ...

  3. Linux查找和替换目录下所有文件中字符串(转载)

    转自:http://rubyer.me/blog/1613/ 单个文件中查找替换很简单,就不说了.文件夹下所有文件中字符串的查找替换就要记忆了,最近部署几十台linux服务器,记录下总结. 查找文件夹 ...

  4. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

  5. JAVA 字符串驻留池

    一切从String str = new String("abc")说起...    这行代码形式上很简单,其实很复杂.有一个常见的Java笔试题就是问上面这行代码创建了几个Stri ...

  6. 三张图彻底了解Java中字符串的不变性

    转载: 三张图彻底了解Java中字符串的不变性 定义一个字符串 String s = "abcd"; s中保存了string对象的引用.下面的箭头可以理解为"存储他的引用 ...

  7. .Net字符串驻留池

    在.Net中,对于相同的字符串,.Net会将它们指向同一个地址,它们是相同的实例..Net中的字符串并不会更新,当更改一个字符串变量时,由于字符串的不可变性,.Net实际上是新创建一个字符串,而将变量 ...

  8. C# 字符串驻留池

    在.Net中,对于相同的字符串,.Net会将它们指向同一个地址,它们是相同的实例..Net中的字符串并不会更新,当更改一个字符串变量时,由于字符串的不可变性,.Net实际上是新创建一个字符串,而将变量 ...

  9. python 的字符串驻留机制

    我们都知道python中的引用计数机制,相同对象的引用其实都是指向内存中的同一个位置,这个也叫做“python的字符串驻留机制”.其他的就不多说了,自行研究. 重点!!!!!! python的引用计数 ...

随机推荐

  1. Azkaban时区问题导致调度差1天

    设置了Azkaban调度是每日凌晨一次,如下: 但是调度历史上显示最近一次调度时间是 初步怀疑是因为时区问题导致,查看服务器时区如下 cat /etc/timezone 为Asia/Shanghai. ...

  2. yii 执行流程

    应用执行流程: 浏览器向服务器发送 Http Request | 控制器(protected/controllers) | |---> Action | 创建模型 (Model) | 检查$_P ...

  3. PAT乙级1033

    1033 旧键盘打字 (20 分)   旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现.现在给出应该输入的一段文字.以及坏掉的那些键,打出的结果文字会是怎样? 输入格式: 输入在 2 ...

  4. Using 1.7 requires compiling with Android 4.4 (KitKat); currently using

    今天编译一个project,我设置为api 14,可是编译报错: Using 1.7 requires compiling with Android 4.4 (KitKat); currently u ...

  5. 求助:将以下ES5格式代码转换为ES6格式!!!

    function Slider(id){     //属性     //  1. 通过id获取元素对象(大盒子)     this.bigBox = document.getElementById(i ...

  6. Redis高级特性---------事务与持久化与发布订阅

    一.redis事务的用法 1.开启事务:multi 2.提交事务:exec   ( queued只是把指令放入队列中,没有执行) 3.取消事务:discard 4.redis事务不能保证同时成功或者失 ...

  7. A1084

    输入:只能输入A-Z(不区分大小写),0-9和下划线: 第一行输入应输入字符串,第二行输入实际输入字符串. 输出:按大写输出缺少的字符,每个字符输出一次. 注意: 1.由于不区分大小写,则需要将小写字 ...

  8. Python urllib模块学习

    urlopen()与urlretrieve() 1.urlopen()方法 urllib.urlopen(url[, data[, proxies]]) :创建一个表示远程url的类文件对象,然后像本 ...

  9. vue动态修改title

    1.项目中,cmd下 ,运行:cnpm install vue-wechat-title --save 2.在 main.js 中,设置: import VueWechatTitle from 'vu ...

  10. Oracle中实现dblink的作法

    基本环境: 机器1: 192.168.56.102  用作dblink的使用者(create database link 语句在此执行) 机器2: 192.168.56.103  用作dblink的源 ...