对于线程来说,完全执行自己的数据,而不必访问任何种类的共享数据,这样的线程非常罕见。例如系统中的所有线程都必须访问系统资源,为了防止共享资源被多个线程破坏,编程人员必须在代码中使用线程同步构造。Windows和CLR提供了许多线程同步构造,许多CLR的线程同步构造实际上就是面向对象的类包装器(wrapper),他们包装了Win32中的线程同步构造,毕竟CLR线程就是Windows线程,这意味着Windows调度并控制着线程的同步。
当线程调用一个互锁方法时,CPU应强制高速缓存一致性。因此如果我们通过互锁方法操作变量的话,所有的线程同步锁(包括:Monitor,ReaderWriterLock,Mutex,Semaphore,AutoResetEvent,ManualResetEvent等)都将内部调用互锁方法。
互锁方法
以线程安全的方式操作数据的最快方法就是使用互锁方法。System.Threading.Interlocked类定义了一组静态方法来支持互锁,这些方法可以以线程安全的方式自动的修改变量。
Monitor类和lock方法
微软提供类Monitor类实现互锁,并提供了lock来进行了该类的封装,使其更加好用。在使用lock方法进行互锁时,参数不应给为this,这样会出现公开锁问题。应该定义一个私有的Object字段成员,并构建对象,然后以私有Object的引用为参数使用lock语句。示例代码:
internal sealed class TransactionWithLockObject {
// 分配一个用于加锁的private对象m_lock
private Object m_lock = new Object();
// 下述字段标识最后一次事务处理执行的时间
private DateTime timeOfLastTransaction;
public void PerformTransaction() {
lock (m_lock) { //对私有字段加锁
// 执行事物处理
// 记录最近一次事物处理的时间
timeOfLastTransaction = DateTime.Now;
} // 对私有对象解锁
}
// 下述属性返回最后一次事务处理执行的时间
public DateTime LastTransaction {
get {
lock (m_lock) { // 对私有字段对象加锁
return timeOfLastTransaction; //
} // 解锁
}
}
}
托管代码中的Windows内核对象
Windows提供了若干个只用于线程同步的内核对象,这些内核对象包括:互斥体、信号量、事件。Monitor和ReaderWriterLock方法只允许线程的同步在同一个应用程序域内,而内核对象可以同步运行于不同应用程序域或者不同进程中的线程。为此,在创建内核对象时,我们可以将它关联一个安全权限来表示什么人可以操作内核对象。无论何时,只要线程等待内核对象,线程必须从用户模式转为内核模式,导致线程产生性能损失。由于这个原因,通过内核对象来进行线程同步是最慢的一种同步线程方式。
单个对象收到信号时调用方法
有时候应用程序生成线程,只是为了等待一个单个的内核对象收到信号,只是为了等待一个单个的内核对象收到信号。一旦对象收到信号,线程就会为另一个线程提交一些通知,然后循环返回,等待这个对象再次收到信号。在这种情况下,线程池是一个很好的办法。
内核对象收到信号时,为了让线程池中的线程调用回调方法,可以调用System.Threading.ThreadPool类的静态方法RegisterWaitForSingleObject。示例代码:
using System;
using System.Threading;
public static class Program {
public static void Main() {
AutoResetEvent are = new AutoResetEvent(false);
// 告诉线程池等待AutoResetEvent 对象
RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(
are, // 等待AutoResetEvent 对象
EventOperation, // 回调方法
null, // 将null传递给EventOperation
5000, // 用5秒钟等待事件发送信号
false); // 每一次当事件的信号被发送后都调用EventOperaton
// 进入循环
Char operation;
do {
Console.WriteLine("S=Signal, Q=Quit?") ;
operation = Char.ToUpper(Console.ReadKey(true).KeyChar);
if (operation == 'S') {
// 用户希望发送事件
are.Set();
}
} while (operation != 'Q');
// 告诉线程池停止等待事件
rwh.Unregister(null);
}
// 一旦事件的信号被发送,或者最后一个信号或超时值已经过去了5秒钟,就调用这个方法
private static void EventOperation(Object state, Boolean timedOut) {
if (timedOut) {
Console.WriteLine("Timedout while waiting for the AutoResetEvent.");
} else {
Console.WriteLine("The AutoResetEvent became signaled.");
}
}
}