zoukankan      html  css  js  c++  java
  • mutex互斥锁

    NET多线程探索-互斥锁,信号量,事件(小新和拆弹部队友情演出)

    2012-03-22 17:14 by 海不是蓝, 835 visits, 收藏编辑

    mutex互斥锁-不准确的时钟

    概念性的东西: 
    互斥锁是一个互斥的同步对象,一个时间只有一个线程可以获取它。 
    前一篇文章中的时钟程序我们这里用Mutex互斥锁来实现。

    class Program
    {
        static void Main(string[] args)
        {
            Clock C = new Clock();
            C.RunClock(1);
            Console.Read();
        }
    }
    public class Clock
    {
        public Mutex Mtx = new Mutex();
    
        //开始运行时钟,输入运行分钟
        public void RunClock(Int32 Minute)
        {
            Thread T1 = new Thread((object Minute1) =>
            {
                Int32 m = Convert.ToInt32(Minute1) * 60 / 2;
                while (m > 0)
                {
                    DI(true);
                    m--;
                }
            });
            Thread T2 = new Thread((object Minute1) =>
            {
                Int32 m = Convert.ToInt32(Minute1) * 60 / 2;
                while (m > 0)
                {
                    DA(true);
                    m--;
                }
            });
            T1.Start(true);
            T2.Start(true);
        }
    
        public void DI(bool run)
        {
            Mtx.WaitOne();
            if (!run)
            {
                Mtx.ReleaseMutex();
                return;
            }
            else
            {
                Console.WriteLine("嘀");
                Thread.Sleep(1000);
                Mtx.ReleaseMutex();
            }
        }
    
        public void DA(bool run)
        {
            Mtx.WaitOne();
            if (!run)
            {
                Mtx.ReleaseMutex();
                return;
            }
            else
            {
                Console.WriteLine("嗒");
                Thread.Sleep(1000);
                Mtx.ReleaseMutex();
            }
        }
    }

    图片1 
    为什么会这样?貌似一切都是合理的!


    这里我只发表下我的理解,还请个位园友自己认真分析下,毕竟我个人理解可能是错误的! 
    如果您发现我理解错了,请在评论指出,我好及时学习更早! 

    Monitor的时钟是使用了lock来锁定,lock最后也会被编译成为Monitor的Enter和Exit。 
    重点是Monitor在Pulse(obj)的时候已经就确定了另外一个等待obj被释放的线程拥有了执行权! 
    而Mutex在ReleaseMutex在释放锁定之后,当前线程和其他等待线程都执行WaitOne,导致了线程执行权的争夺! 
    随着程序的运行,线程之间的争夺激烈。

    mutex互斥锁-杯具的拆弹手



    下面用互斥锁实现个例子,一个炸弹,同时只能一个人拆弹,如果超过1个人就爆炸。

    class Program
    {
        static void Main(string[] args)
        {
            炸弹 b = new 炸弹();
            b.拆弹();
            b.拆弹();
            Console.Read();
        }
    }
    public class 炸弹
    {
        private Int32 热度 = 0;
        public Mutex m = new Mutex();
    
        public void 拆弹()
        {
            Thread t = new Thread(炸弹内部);
            t.Start();
        }
    
        private void 炸弹内部()
        {
            m.WaitOne();
            热度++;
            Thread.Sleep(1000);
            if (热度>1)
            {
                Console.WriteLine("炸弹爆炸!拆弹手见马克思...");
                m.ReleaseMutex();
                return;
            }
            热度--;
            Console.WriteLine("炸弹安全拆除!拆弹手这个月奖金加倍...");
            m.ReleaseMutex();
        }
    }

    图片2


    拆弹手很幸运,这个月奖金加倍了。如果把Mutex去掉,那么马克思就等着他们去聊天。

    信号量-Semaphore


    互斥锁和同步都是锁定一个资源,同时只让一个线程去操作。 
    对于可以允许限定数量线程执行的情况互斥锁就不适合了,这里就需要信号量。 
    信号量通过一个计数器来控制对共享资源的访问,如果计数器的闲置数大于0,那么就允许访问,如果=0就拒绝访问。

    public Semaphore(int initialCount, int maximumCount);

    initialCount:可以同时授予的信号量的初始请求数。 
    maximumCount:可以同时授予的信号量的最大请求数。

    public virtual bool WaitOne();
    public int Release();

    WaitOne():阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号。 
    Release():退出信号量并返回前一个计数。 

    下面还是请出我们杯具的拆弹手来演示。

    class Program
    {
        static void Main(string[] args)
        {
            //有关部门的砖家叫兽告诉拆弹手炸弹最多4个人同时拆
            Int32 拆弹手人数 = 4;
            炸弹 b = new 炸弹(拆弹手人数);
            while (拆弹手人数 > 0)
            {
                b.拆弹();
                拆弹手人数--;
            }
            Console.Read();
        }
    }
    public class 炸弹
    {
        private Int32 最高温度 = 0;
        private Int32 热度 = 0;
        private Semaphore S;
    
        public 炸弹(Int32 limit)
        {
            最高温度 = 3;
            S = new Semaphore(limit, limit);
        }
    
        public void 拆弹()
        {
            Thread t = new Thread(炸弹内部);
            t.Start();
        }
    
        private void 炸弹内部()
        {
            S.WaitOne();
            热度++;
            Thread.Sleep(1000);
            if (热度 > 最高温度)
            {
                Console.WriteLine("炸弹爆炸!拆弹手见马克思...");
                S.Release();
                return;
            }
            热度--;
            Console.WriteLine("炸弹安全拆除!拆弹手这个月奖金加倍...");
            S.Release();
        }
    }

    1

    杯具的拆弹手,这里砖家叫兽错误指出最多4个人,而炸弹最高支持3个人!唉。。。

      
    3

    使用事件实现-幸福的小新


    事件是另外一种同步对象,ManualResetEvent,AutoResetEvent2个类实现了事件同步的功能,都派生自EventWaitHandle类。 

    介绍下ManualResetEvent。

    public ManualResetEvent(bool initialState);
    public virtual bool WaitOne();
    public bool Reset();
    public bool Set();


    第一个是构造函数,传入的变量是事件是否发出信号。 
    第二个是等待一个事件发出信号,期间是阻塞的。 
    第三个是重置事件为未发出信号状态。 
    最后一个是设置事件为发出信号状态。 

    ManualResetEvent和AutoResetEvent的主要实现都是相同,只有一点不同,就是ManualResetEvent需要手动设置事件为未发出信号,而AutoResetEvent是自动设置的。 

    野原新之助的一天

    class Program
    {
        static void Main(string[] args)
        {
            ManualResetEvent mal = new ManualResetEvent(false);
            美女姐姐 美女 = new 美女姐姐();
            小新 新之助 = new 小新();
            Thread t1 = new Thread(new ParameterizedThreadStart(美女.美女姐姐上街));
            Thread t2 = new Thread(new ParameterizedThreadStart(新之助.小新出动));
            t1.Start(mal);
            t2.Start(mal);
            Console.Read();
        }
    
        public class 美女姐姐
        {
            public void 美女姐姐上街(object Mal)
            {
                Thread.Sleep(2000);
                Console.WriteLine("青春美丽的美女姐姐上街了...");
                Thread.Sleep(1000);
                ((ManualResetEvent)Mal).Set();
            }
        }
    
        public class 小新
        {
            public 小新()
            {
                Thread.Sleep(1000);
                Console.WriteLine("小新拿着奶茶,猥琐的躲在大街边的绿化带里...");
                Thread.Sleep(1000);
            }
    
            public void 小新出动(object Mal)
            {
                ManualResetEvent Mal1 = (ManualResetEvent)Mal;
                Console.WriteLine("小新左右寻找美女姐姐...");
                //等待美女姐姐的出现
                 Mal1.WaitOne();
                Console.WriteLine("小新跑到美女姐姐面前,脱下小裤裤,亮出大象,开始跳大象舞...");
                Console.WriteLine("屁股扭扭,“大象,大象,大象...”");
                Mal1.Reset();
            }
        }
    }

    2


    不解释了。。。 
    4

    总结下吧!


    写了这么多,应该好好思考下这些同步方式,每种虽然大概思路是一样!但是绝对有各自的特点。 
    有空大家还是看看CLR 3版中的线程核心部分,里面把这些同步的原理讲的很清楚。 
    总之线程的水太深,慢慢游吧,下一篇把剩下的一些线程同步方式讲下,然后就在最后对NET线程同步做个总结。

    作者:海不是蓝 
    博客:hailan2012 
    邮箱:hailan2012@sina.com 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    随笔分类 -多线程编程

    NET多线程探索-互斥锁,信号量,事件(小新和拆弹部队友情演出)

    2012-03-22 17:14 by 海不是蓝, 835 visits, 网摘收藏编辑
     

    NET多线程探索-线程同步和通信

    2012-03-20 16:53 by 海不是蓝, 1157 visits, 网摘收藏编辑
     

    NET多线程探索-NET线程基础知识点

    2012-03-19 13:40 by 海不是蓝, 216 visits, 网摘收藏编辑
     
  • 相关阅读:
    迭代器和生成器
    New test
    MySQL笔记整理
    Python基础数据类型
    Python基础
    Python入门知识
    Linux / MacOS 下Redis 安装、配置和连接
    NuGet的使用心得
    简单工厂模式和策略模式的区别与结合
    NuGet的使用和服务搭建
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2413200.html
Copyright © 2011-2022 走看看