Mutex [C#]
可以使用 Mutex 对象在线程之间以及跨进程进行同步。虽然 Mutex 不具备 Monitor 类的所有等待和脉冲功能,但它的确提供了创建可在进程之间使用的命名的互斥体的功能。
调用 WaitOne、WaitAll 或 WaitAny 可以请求 Mutex 的所属权。如果没有任何线程拥有它,则 Mutex 的状态为已发信号的状态。
如果某个线程拥有 Mutex,则该线程就可以在重复的等待-请求调用中指定同一个 Mutex,而不必阻塞其执行;但是,它必须释放与释放所属权相同的次数的 Mutex。
如果某个线程在拥有 Mutex 时正常终止,则 Mutex 的状态将被设置为已发信号的状态,并且下一个等待线程将获取所属权。Mutex 类与 Win32 CreateMutex 调用相对应。
下面的 C# 代码示例说明了 Mutex 的使用。
[C#]
using System;
using System.Threading;
public class MutexSample{
static Mutex gM1;
static Mutex gM2;
const int ITERS = 100;
static AutoResetEvent Event1 = new AutoResetEvent(false);
static AutoResetEvent Event2 = new AutoResetEvent(false);
static AutoResetEvent Event3 = new AutoResetEvent(false);
static AutoResetEvent Event4 = new AutoResetEvent(false);
public static void Main(String[] args){
Console.WriteLine("MutexSample.cs ...");
gM1 = new Mutex(true,"MyMutex");
// Create Mutext initialOwned, with name of "MyMutex".
gM2 = new Mutex(true);
// Create Mutext initialOwned, with no name.
Console.WriteLine(" - Main Owns gM1 and gM2");
AutoResetEvent[] evs = new AutoResetEvent[4];
evs[0] = Event1;
// Event for t1.
evs[1] = Event2;
// Event for t2.
evs[2] = Event3;
// Event for t3.
evs[3] = Event4;
// Event for t4.
MutexSample tm = new MutexSample( );
Thread t1 = new Thread(new ThreadStart(tm.t1Start));
Thread t2 = new Thread(new ThreadStart(tm.t2Start));
Thread t3 = new Thread(new ThreadStart(tm.t3Start));
Thread t4 = new Thread(new ThreadStart(tm.t4Start));
t1.Start();
// Calls Mutex.WaitAll(Mutex[] of gM1 and gM2).
t2.Start();
// Calls Mutex.WaitOne(Mutex gM1).
t3.Start();
// Calls Mutex.WaitAny(Mutex[] of gM1 and gM2).
t4.Start();
// Calls Mutex.WaitOne(Mutex gM2).
Thread.Sleep(2000);
Console.WriteLine(" - Main releases gM1");
gM1.ReleaseMutex( );
// t2 and t3 will end and signal.
Thread.Sleep(1000);
Console.WriteLine(" - Main releases gM2");
gM2.ReleaseMutex( );
// t1 and t4 will end and signal.
WaitHandle.WaitAll(evs);
// Waiting until all four threads signal that they are done.
Console.WriteLine("... MutexSample.cs");
}
public void t1Start(){
Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])");
Mutex[] gMs = new Mutex[2];
gMs[0] = gM1;
// Create and load an array of Mutex objects for WaitAll call.
gMs[1] = gM2;
Mutex.WaitAll(gMs);
// Waits until both Mutex objects are released.
Thread.Sleep(2000);
Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[])");
Event1.Set( );
// AutoResetEvent.Set( ) flagging method is done.
}
public void t2Start(){
Console.WriteLine("t2Start started, gM1.WaitOne( )");
gM1.WaitOne( );
// Waits until Mutex gM1 is released.
Console.WriteLine("t2Start finished, gM1.WaitOne( )");
Event2.Set( );
// AutoResetEvent.Set( ) flagging method is done.
}
public void t3Start(){
Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])");
Mutex[] gMs = new Mutex[2];
gMs[0] = gM1;
// Create and load an array of Mutex objects for WaitAny call.
gMs[1] = gM2;
Mutex.WaitAny(gMs);
// Waits until either Mutex object is released.
Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
Event3.Set( );
// AutoResetEvent.Set( ) flagging method is done.
}
public void t4Start(){
Console.WriteLine("t4Start started, gM2.WaitOne( )");
gM2.WaitOne( );
// Waits until Mutex gM2 is released.
Console.WriteLine("t4Start finished, gM2.WaitOne( )");
Event4.Set( );
// AutoResetEvent.Set( ) flagging method is done.
}
}
Interlocked [C#]
Interlocked 方法 CompareExchange、Decrement、Exchange 和 Increment 提供了一种简单机制,以同步对多个线程所共享的变量的访问。如果该变量位于共享内存中,则不同进程的线程就可以使用该机制。
Increment 和 Decrement 函数将递增或递减变量与检查结果值的操作组合起来。该原子操作对于多任务操作系统十分有用,在这种操作系统中,系统可以中断一个线程的执行,以便将一个处理器时间片授予另一个线程。如果没有这样的同步,则一个线程可能在递增某个变量后但尚未能够检查该变量的结果值之前被系统中断。然后,第二个线程可以递增同一个变量。当第一个线程收到其下一个时间片时,它将检查该变量的值,而现在该值已被递增了两次而不是一次。Interlocked 变量访问函数可以防止出现这种错误。
Exchange 函数自动交换指定变量的值。CompareExchange 函数组合以下两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。
在现代处理器中,Interlocked 类的方法通常可以由单个指令来实现。这样,Interlocked 类的方法就可以提供高性能的同步,并可用于生成较高级别的同步机制,如旋转锁。
由 Interlocked 公开的Exchange 和 CompareExchange 方法采用可以存储引用的 Object 类型的参数。但是,类型安全要求将所有参数严格类型化为 Object;不能在对其中一个方法的调用中简单地将对象强制转换为 Object。换言之,您必须创建 Object 类型的变量,将自定义对象赋给该变量,然后传递该变量。下面的简单 C# 代码示例将只允许使用单个调用来设置此属性。
[C#]
private Object _x;
public propery X x{
set(X value) {
Object ovalue = value;
Interlocked.CompareExchange(ref x, ovalue, null);
}
get(){
return (X) _x;
}
}
ReaderWriterLock
ReaderWriterLock 定义实现单个编写器/多个阅读器语义的锁。ReaderWriterLock:
开销非常低,足以大量使用(如针对每个对象的同步)。
尝试在编写器和阅读器之间寻求平衡。一旦请求了编写器锁,就不会再接受任何新的阅读器,直到该编写器具有访问权限为止。编写器和阅读器都不会被永久地拒绝访问。
支持超时,这是用于检测死锁的很有价值的功能。
支持事件缓存。这就使事件可以从争用程度最小的区域移动到争用程度最大的区域。换句话说,进程中的线程数限制了阅读器锁或编写器锁所需要的事件数。
支持由阅读器和编写器嵌套的锁。
支持旋转计数以避免在多处理器计算机上进行上下文切换。
支持用指示中间编写操作的返回参数升级到编写器锁和从编写器锁降级从而还原锁的状态的功能。
支持为调用应用程序代码释放锁的功能。RestoreLock 还原锁的状态并指示中间编写操作。
从最常见的失败(如创建事件失败)中恢复。换句话说,锁维护一致的内部状态并保持可用。