同步构造(上篇)
这些内容说实话,我自己还不是很明白,但是我脑海里初恋了两个小人,第一个小人说,别老是停留在自己会的那个阶段,有时候稍微搞搞你不明白的东西对你来说是一种进步,第二个小人说:”第一个小人说得对啊”
Mutex就像一个C#lock一样,不同的是它可以跨进程.
进入和释放一个Mutex要花费几毫秒,大约比C#的lock慢50倍.
使用一个Mutex的实力,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁.
因为Mutex是跨进程的,所以我们可以使用Mutex来测试程序是否已经运行.
public static void MainThread() { using (var mutex = new Mutex(false, "hahah")) { if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false)) { Console.WriteLine("only one"); return; } RunProgram(); } }
一个Semaphore就像一个酒吧一样,通过门卫来限制它的客人,一旦达到限制,没有人可以进入.
人们会乖乖的排队,一旦有一个人离开了酒吧,排队中的人就可以进入一个人了.
案例:
class TheClub { //只能容乃三个人的酒吧 static SemaphoreSlim _sem = new SemaphoreSlim(3); public static void MainThread() { for (int i = 0; i <=5; i++) { new Thread(Enter).Start(i); } } static void Enter(object id) { Console.WriteLine(id+"想要进来!"); _sem.Wait(); Console.WriteLine(id+"进来了"); Thread.Sleep(10000); Console.WriteLine(id+"离开了!"); _sem.Release(); } }
你如果有心的话,我不用贴完整代码,你也会自己慢慢地一步一步的把这个案例测试完.我说话挺狠啊,我朋友都说我脸上这么多痘痘就是因为我嘴太毒了...
一个AutoResetEvent就像十字转门一样,插入一张票就让一个人通过,”Auto”代表门会自动关上.
在十字门外的人可以调用WaitOne方法来阻塞,等待.一旦有人插入了票(调用Set方法),就可以让外面等待的人(调用WaitOne)通过了.
创建AutoResetEvent有一个参数.
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
其中false在msdn里的解释为:初始状态为非终止.
其实这个false代表了十字转门没有坏,可以进人,让人等待.
而如果是true的话,初始状态为终止,也就是代表已经调用Set了,就是说十字转门坏了,所以接下来如果有人调用了WaitOne方法,这个调用WaitOne方法的人直接可以进入了,不需要再插入票(不需要再调用set)了,之后的调用和false一致,这一点可以认为AutoResetEvent具有记忆功能,他记住了上次门是打开的状态.所以调用waitone方法可以进入.案例:
class ThreadAutoResetEvent { static EventWaitHandle _waitHandle = new AutoResetEvent(false); public static void MainThread() { new Thread(Waiter).Start(); Thread.Sleep(2000); _waitHandle.Set(); } static void Waiter() { Console.WriteLine("waiting..."); _waitHandle.WaitOne(); Console.WriteLine("Notified"); } }
分析:很简单啊,Waiter执行到Waiting...后,就开始调用WaitOne了,所以在门外排队等待.
而主线程在睡了两秒后,开始插入了一张票,所以Waiter就继续执行了,所以打印Notified. 图解如下:
接下来我们使用AutoResetEvent来模拟实现生产消费问题:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Mutex的案例 { class Program { static void Main(string[] args) { using (ProducerConsumerQueue q=new ProducerConsumerQueue()) { q.EnqueueTask("hello"); for (int i = 0; i < 10; i++) { q.EnqueueTask("byebye!"); } } Console.Read(); } } class ProducerConsumerQueue : IDisposable { EventWaitHandle _wh = new AutoResetEvent(false); Thread _worker; readonly object _locker = new object(); Queue<string> _tasks = new Queue<string>(); public ProducerConsumerQueue() { //创建并启动工作线程 _worker = new Thread(Work); _worker.Start(); } public void EnqueueTask(string task) { lock (_locker) { _tasks.Enqueue(task); } _wh.Set();//一旦有任务了,唤醒等待的线程. } public void Dispose() { EnqueueTask(null); _worker.Join(); //等待_worker线程执行结束 _wh.Close(); } void Work() { while (true) { string task = null; lock (_locker) { if (_tasks.Count>0) { task = _tasks.Dequeue(); if (task==null) { return; } } if (task!=null)//如果有任务的话,执行任务 { Console.WriteLine("Performing task: "+task); Thread.Sleep(1000); } else//否则阻塞 { _wh.WaitOne(); } } } } } }
说实话,这段代码我并不是很理解,可以说不懂,如果一偶高手能给我指点一二,在下感激不尽!
一个ManuResetEvent就是一个普通门,调用set方法门就开了,允许任何人进入,调用WaitOne方法就开始等待进入.调用Reset方法就关门了.在一个关闭的门上调用WasitOne方法就会被阻塞.当门下次被打开的时候,所有等待的线程都可以进入了.除了这些不同以外,一个ManualResetEvent和AutoResetEvent类似.
在.NET 4中,ManualResetEvent提供了一个优化版本.ManualResetEventSlim.优化版本有哪些好处不用我说了吧?
小小的结一下
本次内容主要讲解了一点同步构造的内容,说实话,我看起来已经很吃力了,但是没办法,谁让咱头皮硬呢,当然其他地方也硬!