小记《Safe Thread Synchronization -- Jeffrey Richter》
2012-03-27 22:33 Oliver_Zhao 阅读(207) 评论(0) 编辑 收藏 举报More details, please click here.
.NET的同步实现
当创建一个object在托管堆上时,每个object都有2个附加字段。一个字段是方法列表指针(MethodTablePointer)。这个方法列表指针包含了这个类中方法的内存地址。这个指针让我们能够获取在托管堆中的类型信息。实际上,当我们调用System.Object的GetType方法时,这个方法根据方法列表指针来确定object的真实类型。还有一个字段叫做同步块索引(SyncBlockIndex),它是一个32位的整数。
当一个object被构造出来时,object的同步块索引被初始化为一个负数的值(通常为-1)。当一个方法被调用时,CLR在缓存中存找到一个未被使用的同步块,同时根据这个同步块为同步块索引赋值。换言之,当一个object需要同步字段时,同步块就会跟这个object有一定关系。当不再有超过一个线程访问这个object,这个对象的同步块索引将被重置为一个负值,然后其他对象就可以使用这个同步块。
可以通过上图看到,在CLR Data Structures 区域中,每个类型中有一个系统知道的数据结构;同时,以可以看到同步模块结构。在托管堆中,ObjectA,ObjectB,and ObjectC 被创建。每个对象都有方法列表指针。
使用Monitor操作同步块
在了解同步块之后,我们来使用System.Threading.Monitor锁或解锁一个对象。下面这个方法用来锁一个对象:
public static void Enter(object obj);
当调用Enter方法,首先要查看对象的同步块索引是否为负值,如果同步块索引为负值,寻找同步块并对对象的同步块索引赋值。如果查看同步块发现已经存在与其相关的类型,我们要检查它是否有其他线程拥有同步块。如果没有线程拥有当前对象,那么调用的线程将拥有这个对象。如果有另外一个线程想要拥有这个对象,则需要等到线程放弃拥有这个线程块。
如果你想要你的代码更具有防护性,下面的代码能够帮助你:
public static Boolean TryEnter(object obj);
public static Boolean TryEnter(object obj,
int millisecondsTimeout);
public static Boolean TryEnter(object obj,
TimeSpan timeout);
第一个方法与其他两个方法的区别是拥有一个超时的设置。这个时间是指定线程能够等待多长时间。
我们可以使用Exit来释放同步块:
public static void Exit(object obj);
当然,要注意如果调用Exit方法的线程没有对象的同步块,这个Exit方法将会抛出异常。这意味着这里根本不需要释放同步块。
Microsoft方式同步
首先来看一段代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Transaction {
// Private field holding the time of
// the last transaction performed
private DateTime timeOfLastTransaction;
public void PerformTransaction() {
// Lock this object
Monitor.Enter(this);
// Perform the transaction...
// Record time of the most recent transaction
timeOfLastTransaction = DateTime.Now;
// Unlock this object
Monitor.Exit(this);
}
// Public read-only property returning
// the time of the last transaction
public DateTime LastTransaction {
get {
// Lock this object
Monitor.Enter(this);
// Save the time of the last transaction
// in a temporary variable
DateTime dt = timeOfLastTransaction;
// Unlock this object
Monitor.Exit(this);
// Return the value in the temporary variable
return(dt);
}
}
}
Lock语句块
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Transaction {
// Private field holding the time of
// the last transaction performed
private DateTime timeOfLastTransaction;
public void PerformTransaction() {
lock (this) {
// Perform the transaction...
// Record time of the most recent transaction
timeOfLastTransaction = DateTime.Now;
}
}
// Public read-only property returning
// the time of the last transaction
public DateTime LastTransaction {
get {
lock (this) {
// Return the time of the last transaction
return timeOfLastTransaction;
}
}
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Regular function
public void SomeMethod() {
// Lock the object
Object oTemp = this;
Monitor.Enter(oTemp);
try {
// Access the object
...
// Unlock the object
}
finally {
Monitor.Exit(oTemp);
}
// Return
}
// Simple function
public void SomeMethod() {
// Lock the object
lock (this) {
// Access the object
...
// Unlock the object
}
// Return
}
静态方法同步
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Transaction {
// Private field holding the time of
// the last transaction performed
private static DateTime timeOfLastTransaction;
public static void PerformTransaction() {
lock (typeof(Transaction)) {
// Perform the transaction...
// Record time of the most recent transaction
timeOfLastTransaction = DateTime.Now;
}
}
// Public read-only property returning
// the time of the last transaction
public static DateTime LastTransaction {
get {
lock (typeof(Transaction)) {
// Return the time of the last transaction
return timeOfLastTransaction;
}
}
}
}
缺陷
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Threading;
class App {
static void Main() {
// Construct an instance of the App object
App a = new App();
// This malicious code enters a lock on
// the object but never exits the lock
Monitor.Enter(a);
// For demonstration purposes, let's release the
// root to this object and force a garbage collection
a = null;
GC.Collect();
// For demonstration purposes, wait until all Finalize
// methods have completed their execution - deadlock!
GC.WaitForPendingFinalizers();
// We never get to the line of code below!
Console.WriteLine("Leaving Main");
}
// This is the App type's Finalize method
~App() {
// For demonstration purposes, have the CLR's
// Finalizer thread attempt to lock the object.
// NOTE: Since the Main thread owns the lock,
// the Finalizer thread is deadlocked!
lock (this) {
// Pretend to do something in here...
}
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Transaction {
// Private, static Object field
// used purely for synchronization
private static Object objLock = new Object();
// Private field holding the time of
// the last transaction performed
private static DateTime timeOfLastTransaction;
public static void PerformTransaction() {
lock (objLock) {
// Perform the transaction...
// Record time of the most recent transaction
timeOfLastTransaction = DateTime.Now;
}
}
// Public read-only property returning
// the time of the last transaction
public static DateTime LastTransaction {
get {
lock (objLock) {
// Return the time of the last transaction
return timeOfLastTransaction;
}
}
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Transaction {
// Private Object field used
// purely for synchronization
private Object objLock = new Object();
// Private field holding the time of
// the last transaction performed
private DateTime timeOfLastTransaction;
public void PerformTransaction() {
lock (objLock) {
// Perform the transaction...
// Record time of the most recent transaction
timeOfLastTransaction = DateTime.Now;
}
}
// Public read-only property returning
// the time of the last transaction
public DateTime LastTransaction {
get {
lock (objLock) {
// Return the time of the last transaction
return timeOfLastTransaction;
}
}
}
}
值类型做标识
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class AnotherType {
// An unboxed Boolean value type
private Boolean flag = false;
public Boolean Flag {
set {
Monitor.Enter(flag); // Boxes flag and locks the object
flag = value; // The actual value is unprotected
Monitor.Exit(flag); // Boxes flag, attempts to unlock
// the object
}
}
}
非装箱的对象没有方法列表指针和同步块索引,这意味着未装箱的值类型不能够拥有同步块与其相关。所以,当一个值类型传入Enter方法或Exit方法后,将执行装箱操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class AnotherType {
// An unboxed Boolean value type
private Boolean flag = false;
// A private Object field used to
// synchronize access to the flag field
private Object flagLock = new Object();
public Boolean Flag {
set {
Monitor.Enter(flagLock);
flag = value;
Monitor.Exit(flagLock);
}
}
}
传入bool值时,编译器会报错:error CS0185: 'bool' is not a reference type as required by the lock statement.
More details, please click here.