Mutex 就像一个C# lock一样不同的是它可以跨进程.进入和释放一个Mutex要花费几毫秒大约比C#的lock慢50倍。使用一个Mutex的实例调用WaitOne方法来获取锁ReleaseMutex方法来释放锁。因为Mutex是跨进程的所以我们可以使用Mutex来检测程序是否已经运行。publicstaticvoidMainThread(){using(var mutexnewMutex(false,LoveJenny OneAtATimeDemo)){if(!mutex.WaitOne(TimeSpan.FromSeconds(3),false)){Console.WriteLine(只能运行一个应用程序!);return;}RunProgram();}}2Semaphore:一个Semaphore就像一个酒吧一样通过门卫来限制它的客人一旦到达限制没有人可以进入人们会在门外乖乖的排队一旦有一个人离开酒吧排队中的人就可以进入了一个了。下面是个例子classTheClub{//只能容纳三个人的酒吧staticSemaphoreSlim _semnewSemaphoreSlim(3);publicstaticvoidMainThread(){for(inti1; i5; i)newThread(Enter).Start(i);//有5个人向进入}staticvoidEnter(objectid){Console.WriteLine(id想要进入了);_sem.Wait();Console.WriteLine(id已经进入了!);Thread.Sleep(1000*(int)id);Console.WriteLine(id离开了?);_sem.Release();}}3AutoResetEvent一个AutoResetEvent就像十字转门一样插入一张票就让一个人通过”Auto”代表门会自动的关上。在十字门外面的人可以调用WaitOne方法来阻塞等待。一旦有人插入了票(调用Set方法)就可以让外面等待的人(调用WaitOne方法的线程)通过了。创建AutoResetEvent有一个参数。staticEventWaitHandle_waitHandle newAutoResetEvent(false);其中false在msdn的解释是初始状态为非终止按照我个人的理解false代表了十字转门非终止所以可以正常的进入等待。而如果是true的话初始状态为终止,也就是代表已经调用了Set了就是说十字转门已经停止了所以接下来如果有人调用了WaitOne方法这个调用WaitOne方法的人直接就可以进入了不需要再插入票(不需要调用Set)了之后的调用和false一致这一点可以认为AutoResetEvent具有记忆功能它记住了上次门是打开的状态。所以调用waitone方法可以进入。classThreadAutoResetEvent{staticEventWaitHandle _waitHandlenewAutoResetEvent(false);publicstaticvoidMainThread(){newThread(Waiter).Start();Thread.Sleep(2000);_waitHandle.Set();}staticvoidWaiter(){Console.WriteLine(Waiting...);_waitHandle.WaitOne();Console.WriteLine(Notified);}}很简单Waiter执行到Waiting…后就开始调用WaitOne了所以在门外排队等待。而主线程在睡了两秒后开始插入一张票(Set).所以Waiter就继续执行所以打印Notified接下来我们使用AutoResetEvent来模拟实现生产消费问题classProducerConsumerQueue:IDisposable{EventWaitHandle _whnewAutoResetEvent(false);Thread _worker;readonlyobject_lockernewobject();Queuestring_tasksnewQueuestring();publicProducerConsumerQueue(){//创建并启动工作线程_workernewThread(Work);_worker.Start();}publicvoidEnqueueTask(stringtask){lock(_locker) _tasks.Enqueue(task);_wh.Set();//一旦有任务了唤醒等待的线程}publicvoidDispose(){EnqueueTask(null);_worker.Join();//等待_worker线程执行结束_wh.Close();}voidWork(){while(true){stringtasknull;lock(_locker){if(_tasks.Count0){task_tasks.Dequeue();if(tasknull)return;}if(task!null)//如果有任务的话执行任务{Console.WriteLine(Performing task:task);Thread.Sleep(1000);}else//否则阻塞,去睡觉吧{_wh.WaitOne();}}}}}主线程调用如下publicstaticvoidMain(){using(ProducerConsumerQueue qnewProducerConsumerQueue()){q.EnqueueTask(Hello);for(inti0; i10; i) q.EnqueueTask(Sayi);q.EnqueueTask(Goodbye!);}}
深入浅出多线程系列之五:一些同步构造(上篇)
Mutex 就像一个C# lock一样不同的是它可以跨进程.进入和释放一个Mutex要花费几毫秒大约比C#的lock慢50倍。使用一个Mutex的实例调用WaitOne方法来获取锁ReleaseMutex方法来释放锁。因为Mutex是跨进程的所以我们可以使用Mutex来检测程序是否已经运行。publicstaticvoidMainThread(){using(var mutexnewMutex(false,LoveJenny OneAtATimeDemo)){if(!mutex.WaitOne(TimeSpan.FromSeconds(3),false)){Console.WriteLine(只能运行一个应用程序!);return;}RunProgram();}}2Semaphore:一个Semaphore就像一个酒吧一样通过门卫来限制它的客人一旦到达限制没有人可以进入人们会在门外乖乖的排队一旦有一个人离开酒吧排队中的人就可以进入了一个了。下面是个例子classTheClub{//只能容纳三个人的酒吧staticSemaphoreSlim _semnewSemaphoreSlim(3);publicstaticvoidMainThread(){for(inti1; i5; i)newThread(Enter).Start(i);//有5个人向进入}staticvoidEnter(objectid){Console.WriteLine(id想要进入了);_sem.Wait();Console.WriteLine(id已经进入了!);Thread.Sleep(1000*(int)id);Console.WriteLine(id离开了?);_sem.Release();}}3AutoResetEvent一个AutoResetEvent就像十字转门一样插入一张票就让一个人通过”Auto”代表门会自动的关上。在十字门外面的人可以调用WaitOne方法来阻塞等待。一旦有人插入了票(调用Set方法)就可以让外面等待的人(调用WaitOne方法的线程)通过了。创建AutoResetEvent有一个参数。staticEventWaitHandle_waitHandle newAutoResetEvent(false);其中false在msdn的解释是初始状态为非终止按照我个人的理解false代表了十字转门非终止所以可以正常的进入等待。而如果是true的话初始状态为终止,也就是代表已经调用了Set了就是说十字转门已经停止了所以接下来如果有人调用了WaitOne方法这个调用WaitOne方法的人直接就可以进入了不需要再插入票(不需要调用Set)了之后的调用和false一致这一点可以认为AutoResetEvent具有记忆功能它记住了上次门是打开的状态。所以调用waitone方法可以进入。classThreadAutoResetEvent{staticEventWaitHandle _waitHandlenewAutoResetEvent(false);publicstaticvoidMainThread(){newThread(Waiter).Start();Thread.Sleep(2000);_waitHandle.Set();}staticvoidWaiter(){Console.WriteLine(Waiting...);_waitHandle.WaitOne();Console.WriteLine(Notified);}}很简单Waiter执行到Waiting…后就开始调用WaitOne了所以在门外排队等待。而主线程在睡了两秒后开始插入一张票(Set).所以Waiter就继续执行所以打印Notified接下来我们使用AutoResetEvent来模拟实现生产消费问题classProducerConsumerQueue:IDisposable{EventWaitHandle _whnewAutoResetEvent(false);Thread _worker;readonlyobject_lockernewobject();Queuestring_tasksnewQueuestring();publicProducerConsumerQueue(){//创建并启动工作线程_workernewThread(Work);_worker.Start();}publicvoidEnqueueTask(stringtask){lock(_locker) _tasks.Enqueue(task);_wh.Set();//一旦有任务了唤醒等待的线程}publicvoidDispose(){EnqueueTask(null);_worker.Join();//等待_worker线程执行结束_wh.Close();}voidWork(){while(true){stringtasknull;lock(_locker){if(_tasks.Count0){task_tasks.Dequeue();if(tasknull)return;}if(task!null)//如果有任务的话执行任务{Console.WriteLine(Performing task:task);Thread.Sleep(1000);}else//否则阻塞,去睡觉吧{_wh.WaitOne();}}}}}主线程调用如下publicstaticvoidMain(){using(ProducerConsumerQueue qnewProducerConsumerQueue()){q.EnqueueTask(Hello);for(inti0; i10; i) q.EnqueueTask(Sayi);q.EnqueueTask(Goodbye!);}}