您的位置:首页 > 其它

同步构造(上篇)

2015-08-25 21:45 429 查看

同步构造(上篇)

前言

这些内容说实话,我自己还不是很明白,但是我脑海里初恋了两个小人,第一个小人说,别老是停留在自己会的那个阶段,有时候稍微搞搞你不明白的东西对你来说是一种进步,第二个小人说:”第一个小人说得对啊”







Metux

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

一个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

一个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();
                    }
                }
            }
        }
    }
 
}


说实话,这段代码我并不是很理解,可以说不懂,如果一偶高手能给我指点一二,在下感激不尽!







ManualResetEvent

一个ManuResetEvent就是一个普通门,调用set方法门就开了,允许任何人进入,调用WaitOne方法就开始等待进入.调用Reset方法就关门了.在一个关闭的门上调用WasitOne方法就会被阻塞.当门下次被打开的时候,所有等待的线程都可以进入了.除了这些不同以外,一个ManualResetEvent和AutoResetEvent类似.



在.NET 4中,ManualResetEvent提供了一个优化版本.ManualResetEventSlim.优化版本有哪些好处不用我说了吧?







小小的结一下



本次内容主要讲解了一点同步构造的内容,说实话,我看起来已经很吃力了,但是没办法,谁让咱头皮硬呢,当然其他地方也硬!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: