C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

时间:2024-06-23 08:07:31

看下组织结构:

System.Object
  System.MarshalByRefObject
    System.Threading.WaitHandle
      System.Threading.Mutex
      System.Threading.Semaphore
      System.Threading.EventWaitHandle
        System.Threading.ManualResetEvent

        System.Threading.AutoResetEvent

System.Object

  System.Threading.Interlocked
  System.Threading.Monitor

  System.Threading.ReaderWriterLock

1, lock 关键字其实就是对 Monitor 类的 Enter()和 Exit()方法的封装。通过 try......catch......finally 语句块确保在 lock 语句块结束后执行 Monitor.Exit()方法,释放互斥锁。下面2段代码等价:

lock(locker)
{
//do something
}
Monitor.Enter(locker);
try
{
// do something
}
finally
{
Monitor.Exit(locker);
}

2,

Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。通过对 lock 关键字的分析我们知道,lock 就是对 Monitor 的 Enter 和 Exit 的一个封装,而且使用起来更简洁,因此 Monitor 类的 Enter()和 Exit()方法的组合使用可以用 lock 关键字替代。 
   Monitor 类的常用方法: 
        TryEnter(): 
            能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用 TryEnter,可以有效防止死锁或者长时间的等待。比如我们可以设置一个等待时间 bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待 1000 秒后根据返回的 bool 值来决定是否继续下面的操作。

Wait() :

释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。 
        Pulse(): 
        PulseAll(): 
            向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。注意:Pulse、PulseAll 和 Wait 方法必须从同步的代码块内调用。

       static object locker = new object();
static bool isHave = false; static void Produce()
{
lock (locker)
{
while (true)
{
//如果已有产品,则等待消费完成
if (isHave)
Monitor.Wait(locker);
Console.WriteLine("生产一个");
Thread.Sleep();
isHave = true;
Monitor.Pulse(locker);
}
}
}
static void Consume()
{
lock (locker)
{
while (true)
{
//如果没有产品,则等待生产完成
if (!isHave)
Monitor.Wait(locker);
Console.WriteLine("消费一个");
Thread.Sleep();
isHave = false;
Monitor.Pulse(locker);
}
}
}

在main函数中调用:

            new Thread(Produce).Start();
new Thread(Consume).Start();

3, Mutex互斥体

public class Test
{
// Create a new Mutex. The creating thread does not own the
// Mutex.
private static Mutex mut = new Mutex(); public static void MyThreadProc()
{
for (int i = ; i < ; i++)
{
UseResource();
}
} // This method represents a resource that must be synchronized
// so that only one thread at a time can enter.
private static void UseResource()
{
// Wait until it is safe to enter.
mut.WaitOne(); Console.WriteLine("{0} has entered the protected area",
Thread.CurrentThread.Name); // Place code to access non-reentrant resources here. // Simulate some work.
Thread.Sleep(); Console.WriteLine("{0} is leaving the protected area\r\n",
Thread.CurrentThread.Name); // Release the Mutex.
mut.ReleaseMutex();
}
}
Test test = new Test();
for (int i = ; i < ; i++)
{
Thread myThread = new Thread(new ThreadStart(Test.MyThreadProc));
myThread.Name = String.Format("Thread{0}", i + );
myThread.Start();
}

mutex还可以判断系统是否已经有一个进程存在。

4,Semaphore 信号量

static Semaphore sph = new Semaphore(, );
static void TProc()
{
while (true)
{
if (sph.WaitOne(, false))
{
try
{
Console.WriteLine("thread" + Thread.CurrentThread.Name + ":enter");
Thread.Sleep();
}
finally
{
sph.Release();
Console.WriteLine("thread" + Thread.CurrentThread.Name + ":exit");
}
}
else
{
Console.WriteLine("thread" + Thread.CurrentThread.Name + ":time out");
}
}
}
Thread t = null;
for (int i = ; i < ; i++)
{
t = new Thread(TProc);
t.Name = i.ToString();
t.Start();
}
Console.WriteLine("main sleep 4s");
Thread.Sleep();
sph.Release();

5,Interlocker类为多个线程共享的变量提供原子操作,它是一个静态类,主要的成员方法如下:
Add:以原子操作的形式,添加两个整数并用两者的和替换第一个整数。
Exchange:以原子操作的形式将变量设置为指定的值,并返回先前值
CompareExchange:比较两个值是否相等,如果相等,则替换其中一个值
Equals:确定两个Object 实例是否相等
Increment:以原子操作的形式递增指定变量的值并存储结果
Decrement:以原子操作的形式递减指定变量的值并存储结果
Read:返回一个以原子操作形式加载的 64 位值

Interlocked.CompareExchange(ref obj, new object(), null);

6, ReaderWriterLock

static ReaderWriterLock rwLock = new ReaderWriterLock();
static object locker = new object();
static void Main(string[] args)
{
Thread t = null;
for(int i = ; i < ;i++)
{
t = newThread(Writer);
t.Name =i.ToString();
t.Start();
}
for(int i = ; i<;i++)
{
t = newThread(Reader);
t.Name =i.ToString();
t.Start();
}
Console.ReadLine();
}
static void Writer()
{
while(true)
{
try
{
rwLock.AcquireWriterLock();
Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
try
{
Thread.Sleep();
}
finally
{
rwLock.ReleaseWriterLock();
Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is exit");
}
}
catch(ApplicationException)
{
Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " wait time out");
}
}
}
static void Reader()
{
while (true)
{
rwLock.AcquireReaderLock(-);
Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
try
{
Thread.Sleep();
}
finally
{
Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is exit");
rwLock.ReleaseReaderLock();
}
}
}

7,AutoResetEvent 自动重置事件

在构造事件对象时需要指定initialState参数是True还是False,以指示事件的初始状态是有信号还是无信号

当一个自动重置事件得到信号时,等待该事件的线程中只有一个线程变为可调度线程,当手动重置对象得到信号时,等待该事件的所有线程均变为可调度线程

class Example
{
private static AutoResetEvent event_1 = new AutoResetEvent(true);
private static AutoResetEvent event_2 = new AutoResetEvent(false); static void Main()
{
Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
"The threads wait on AutoResetEvent #1, which was created\r\n" +
"in the signaled state, so the first thread is released.\r\n" +
"This puts AutoResetEvent #1 into the unsignaled state.");
Console.ReadLine(); for (int i = ; i < ; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(); for (int i = ; i < ; i++)
{
Console.WriteLine("Press Enter to release another thread.");
Console.ReadLine();
event_1.Set();
Thread.Sleep();
} Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
for (int i = ; i < ; i++)
{
Console.WriteLine("Press Enter to release a thread.");
Console.ReadLine();
event_2.Set();
Thread.Sleep();
} // Visual Studio: Uncomment the following line.
//Console.Readline();
} static void ThreadProc()
{
string name = Thread.CurrentThread.Name; Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} is released from AutoResetEvent #1.", name); Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
event_2.WaitOne();
Console.WriteLine("{0} is released from AutoResetEvent #2.", name); Console.WriteLine("{0} ends.", name);
}
}

8, ManualResetEvent 手动重置事件

public class Example
{
// mre is used to block and release threads manually. It is
// created in the unsignaled state.
private static ManualResetEvent mre = new ManualResetEvent(false); static void Main()
{
Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n"); for(int i = ; i <= ; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
} Thread.Sleep();
Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" +
"\nto release all the threads.\n");
Console.ReadLine(); mre.Set(); Thread.Sleep();
Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
"\ndo not block. Press Enter to show this.\n");
Console.ReadLine(); for(int i = ; i <= ; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
} Thread.Sleep();
Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
"\nwhen they call WaitOne().\n");
Console.ReadLine(); mre.Reset(); // Start a thread that waits on the ManualResetEvent.
Thread t5 = new Thread(ThreadProc);
t5.Name = "Thread_5";
t5.Start(); Thread.Sleep();
Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
Console.ReadLine(); mre.Set(); // If you run this example in Visual Studio, uncomment the following line:
//Console.ReadLine();
} private static void ThreadProc()
{
string name = Thread.CurrentThread.Name; Console.WriteLine(name + " starts and calls mre.WaitOne()"); mre.WaitOne(); Console.WriteLine(name + " ends.");
}
}

9, .NET 在一些集合类,如 Queue、ArrayList、HashTable 和 Stack,已经提供了一个供 lock 使用的对象 SyncRoot。

Queue q = new Queue();
lock (q.SyncRoot)
{
foreach (object item in q)
{
//do something
}
}

参考资料:http://blog.****.net/zzy7075/article/details/29842165