13.4 对锁和字段风格的事件的微小改变

13.4.1 健壮的锁

 1     class Program
 2     {
 3         static object locker = new object();
 4         static void Main(string[] args)
 5         {
 6             List<string> list = new List<string>();
 7             lock (locker)
 8             {
 9                 list.Add("item");
10             }
11 
12             //在C# 4之前——包括使用C# 4处理.NET 4之前的东西时——以上语句将被有效地编译为下面 的代码:
13 
14             object tmp = locker;
15             Monitor.Enter(tmp);
16             try
17             {
18                 list.Add("item");
19             }
20             finally
21             {
22                 Monitor.Exit(tmp);
23             }
24         }
25     }

这没有问题,并且它还避免了一些问题。我们要确保释放的监视器与获取的是同一个,因此
首先将被锁定内容的引用复制到一个临时局部变量内 。这同时意味着锁的表达式只会进行一
次求值。然后我们在 try 语句块之前获取锁。因此如果获取锁的线程异常终止,则不会执行
finally 块中释放锁的语句。这还将导致另一个问题:如果线程在获取锁之后和进入 try 块之前
异常终止,我们也无法释放锁。这可能会导致死锁——其他线程将一直等待该线程释放锁。尽管
CLR一直以来都在努力阻止类似事情发生,但也不是完全没有可能发生。
我们所需要的,是一种原子地获取锁并知道它已经被获取的方式。幸运的是,.NET 4新增加
了 Monitor.Enter 的重载,C# 4的编译器将使用这种方式:

 1     class Program
 2     {
 3         static object locker = new object();
 4         static void Main(string[] args)
 5         {
 6             List<string> list = new List<string>();
 7             object tmp = locker;
 8 
 9             bool acquired = false;
10             try
11             {
12                 Monitor.Enter(tmp, ref acquired);
13                 list.Add("item");
14             }
15             finally
16             {
17                 if (acquired)
18                 {
19                     Monitor.Exit(tmp);
20                 }
21             }
22         }
23     }

13.4.2 字段风格的事件

值得简单一提的是,C# 4对字段风格事件的实现方式作了两处修改。尽管它们是潜在的破坏性更改,但似乎不会对你产生什么影响。
总之,字段风格的事件像字段一样进行声明,不再包含显式的 add/remove 语句块,如下:

        public event EventHandler Click;

首先,线程安全的实现方式发生了改变。在C# 4之前,字段风格的事件生成的代码锁定的是
this (实例事件)或声明事件的类型(静态事件)。而C# 4中,编译器实现了线程安全,对原子
的订阅和退订使用了 Interlocked.CompareExchange<T> 。与之前对 lock 语句的修改不同,

面对旧版本的.NET Framework时,这项更改同样适用。
其次,在声明事件的类中,事件名称的含义改变了。以前,在声明事件的类中订阅(或退订)
事件——如 Click += DefaultClickHandler; ——将直接使用后台字段,完全跳过 add/remove
实现。现在情况变了,使用 += 或 -= 时,事件的名称就指向事件本身,而不再是后台字段。当名
称用于其他意图时(通常为分配或调用),则仍然指向后台字段。
尽管在平时使用时你可能不会注意这两处改变,不过它们是合理的,可以使一切变得整洁。
Chris Burrows在他的博客中深入研究了这个话题,想了解更多内容可以参考http://mng.bz/Kyr4。

 

posted @ 2018-12-13 21:11  一只桔子2233  阅读(214)  评论(0编辑  收藏  举报