信号量 <第六篇>

一、ManualResetEvent

该对象有两种信号量状态True和False。构造函数设置初始状态。

  • WaitOne:该方法用于阻塞线程,默认是无限期的阻塞,支持超时阻塞,如果超时就放弃阻塞,这样也就避免了无限期等待的尴尬;
  • Set:手动修改信号量为True,也就是恢复线程执行;
  • ReSet:重置状态;

    class Program
    {
        public static void Main()
        {
            Thread t = new Thread(Run);
            t.Name = "辅助线程";
            t.Start();

            Console.WriteLine("当前时间:{0}  {1}准备执行!", DateTime.Now.TimeOfDay, t.Name);
            //手动修改信号量为True,也就是恢复一个等待线程执行。
            mr.Set();

            Console.ReadKey();
        }

        //一开始设置为false才会等待收到信号才执行
        static ManualResetEvent mr = new ManualResetEvent(true);

        static void Run()
        {
            //线程开始执行时待命,收到信号才动身
            mr.WaitOne();
            Console.WriteLine("\n当前时间:{0}  {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

            //我想让辅助线程暂停3秒
            mr.WaitOne(3000);
            Console.WriteLine("\n当前时间:{0}  {1}暂停3秒,但是无效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

            //我想让辅助线程暂停
            mr.Reset();
            Console.WriteLine("\n当前时间:{0}  {1}还是无效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);
        }
    }

  输出如下:

  

  Reset()的意思其实是重置,重置后才又能再WaitOne();

    class Program
    {
        public static void Main()
        {
            Thread t = new Thread(Run);
            t.Name = "辅助线程";
            t.Start();

            Console.WriteLine("当前时间:{0}  {1}准备执行!", DateTime.Now.TimeOfDay, t.Name);
            //手动修改信号量为True,也就是恢复一个等待线程执行。
            mr.Set();

            Thread.Sleep(10000);
            mr.Set();

            Console.ReadKey();
        }

        //一开始设置为false才会等待收到信号才执行
        static ManualResetEvent mr = new ManualResetEvent(false);

        static void Run()
        {
            //线程开始执行时待命,收到信号才动身
            mr.WaitOne();
            Console.WriteLine("\n当前时间:{0}  {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

            //重置后停止才有效
            mr.Reset();
            //我想让辅助线程暂停3秒
            mr.WaitOne(3000);
            Console.WriteLine("\n当前时间:{0}  {1}暂停3秒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

            //重置后停止才有效
            mr.Reset();
            //我想让辅助线程暂停,10后由主线程再次唤醒
            mr.WaitOne();
            Console.WriteLine("\n当前时间:{0}  {1}暂停,但会被主线程再次唤醒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name    );
        }
    }

  输出如下:

  

二、AutoResetEvent

  AutoResetEvent与ManualResetEvent的区别在于AutoResetEvent 的WaitOne会改变信号量的值。

  比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。

    class Program
    {
        public static void Main()
        {
            Thread t = new Thread(Run);
            t.Name = "辅助线程";
            t.Start();

            Console.WriteLine("当前时间:{0}  {1}准备执行!", DateTime.Now.TimeOfDay, t.Name);

            Console.ReadKey();
        }

        static AutoResetEvent ar = new AutoResetEvent(true);

        static void Run()
        {
            var state = ar.WaitOne(1000);
            Console.WriteLine("当前的信号量状态:{0}", state);

            state = ar.WaitOne(1000);
            Console.WriteLine("再次WaitOne后现在的状态是:{0}", state);
        }
    }

  输出如下:

  

  假如要实现上面ManualResetEvent同样的效果,Run方法就不用手动Reset()了:

    static void Run()
    {
        //线程开始执行时待命,收到信号才动身
        mr.WaitOne();
        Console.WriteLine("\n当前时间:{0}  {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

        //我想让辅助线程暂停3秒
        mr.WaitOne(3000);
        Console.WriteLine("\n当前时间:{0}  {1}暂停3秒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);

        //我想让辅助线程暂停,10后由主线程再次唤醒
        mr.WaitOne();
        Console.WriteLine("\n当前时间:{0}  {1}暂停,但会被主线程再次唤醒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);
    }

  少了手动Reset()代码。

三、Semaphore

  用于控制线程的访问数量,默认的构造函数为initialCount和maximumCount,表示默认设置的信号量个数和最大信号量个数。当你WaitOne的时候,信号量自减,当Release的时候,信号量自增,然而当信号量为0的时候,后续的线程就不能拿到WaitOne了,所以必须等待先前的线程通过Release来释放。

    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Thread t3 = new Thread(Run3);
            t3.Start();

            Console.Read();
        }

        //初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号
        static Semaphore sem = new Semaphore(2, 10);

        static void Run1()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run1" + DateTime.Now.TimeOfDay);
        }

        static void Run2()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run2" + DateTime.Now.TimeOfDay);

            //两秒后
            Thread.Sleep(2000);
            sem.Release();
        }

        static void Run3()
        {
            sem.WaitOne();
            Console.WriteLine("大家好,我是Run3" + DateTime.Now.TimeOfDay);
        }
    }

  输出:

  

  在以上的方法中Release()方法相当于自增一个信号量,Release(5)自增5个信号量。但是,Release()到构造函数的第二个参数maximumCount的值就不能再自增了。

  命名Semaphore可用于进程级交互。

    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Run1);
            t1.Start();

            Thread t2 = new Thread(Run2);
            t2.Start();

            Console.Read();
        }

        //初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号
        static Semaphore sem = new Semaphore(3, 10, "命名Semaphore");

        static void Run1()
        {
            sem.WaitOne();

            Console.WriteLine("进程:" +Process.GetCurrentProcess().Id + "  我是Run1" + DateTime.Now.TimeOfDay);
        }

        static void Run2()
        {
            sem.WaitOne();

            Console.WriteLine("进程:" + Process.GetCurrentProcess().Id + "  我是Run2" + DateTime.Now.TimeOfDay);
        }
    }

  输出如下:

  

  •   ManualResetEvent:每次可以唤醒一个或多个线程;
  •   AutoResetEvent:每次只能唤醒一个线程;
时间: 2024-10-09 16:20:23

信号量 <第六篇>的相关文章

秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/8646902 欢迎关注微博:http://weibo.com/MoreWindows 在<秒杀多线程系列>的前十五篇中介绍多线程的相关概念,多线程同步互斥问题<秒杀多

ElasticSearch入门 第六篇:复合数据类型——数组,对象和嵌套

这是ElasticSearch 2.4 版本系列的第六篇: ElasticSearch入门 第一篇:Windows下安装ElasticSearch ElasticSearch入门 第二篇:集群配置 ElasticSearch入门 第三篇:索引 ElasticSearch入门 第四篇:使用C#添加和更新文档 ElasticSearch入门 第五篇:使用C#查询文档 ElasticSearch入门 第六篇:复合数据类型——数组,对象和嵌套 在ElasticSearch中,使用JSON结构来存储数据,

Python之路【第十六篇】:Django【基础篇】

Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. 基本配置 一.创建django程序 终端命令:django-admin startproject sitename IDE创建Django程序时,本质上都是自动执行上述命令 其他常用命令: python manage.py runserver

Python之路【第六篇】:socket

Python之路[第六篇]:socket Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix,而Unix/Linux基本哲学之一就是"一切皆文件",对于文件用[打开][读写][关闭]模式来操作.socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO.打开.关闭

第六篇 Replication:合并复制-发布

本篇文章是SQL Server Replication系列的第六篇,详细内容请参考原文. 合并复制,类似于事务复制,包括一个发布服务器,一个分发服务器和一个或多个订阅服务器.每一个发布服务器上可以定义多个发布.你也可以同时在同一台发布服务器上定义合并发布.事务发布和快照发布.同事务复制,每一个发布所包含的项目需要是单一数据库中的对象.不同的发布可以定义在不同的数据库上.一个项目可以被多个发布使用.All publications on a publisher, independent of th

《你的灯亮着吗》第五篇、第六篇总结

第五篇讲的是问题是从哪儿来的?由一个叫珍妮特去波兰拜访她的祖母在旅途中遇到的麻烦引出主题.一开始她把问题推到“官僚主义”上,她感觉这似乎是最恰当的.但是最后她发现她十分倾向于把整件事归咎于“官僚主义”,之后她提出了一个至关重要的问题:问题是从哪儿来的?从这点出发,她成功找到乐各种备选答案.后来,珍妮特想这个问题的根源也许是她自己.她开始以正常的心态面对曾被她称作灰脸先生的人,他们顺利的交谈,最后事情的到了解决.一切都正常的进行着,一旦你确定问题真正从哪来,尤其是因为问题的根源常常在你自己身上.

IOC容器特性注入第六篇:利用MVC注入点,把容器启动

这里是利用MVC三个注入点中的:IDependencyResolver 进行注入 在全局类中Global.asax代码如下: #region MVC Inject System.Web.Mvc.DependencyResolver.SetResolver(new DaHua.Sites.DependencyResolve(DaHua.Common.Runtime.EngineContext.Current, System.Web.Mvc.DependencyResolver.Current));

cocos2d-x 3.2 2048——第六篇(最终章)

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 真的要结束了, 终于把2048的开发教程,写完了~ 这次是对于安卓平台的移植: cocos2d-x 3.2 做APK,很简单了呢, 前面环境配置完,我们可以直接在命令行,进行生成, 但是,首先,我们要进行 一些图标 名称 的改变: 本篇博文,不会借助Eclips

C#语法糖之第六篇: 泛型委托- Predicate&lt;T&gt;、Func&lt;T&gt;

今天继续分享泛型委托的Predicate<T>,上篇文章讲了Action委托,这个比Action委托功不一样的地方就是委托引用方法是Bool返回值的方法,Action为无返回值.首先我们看一下它的定义吧: 1 public delegate bool Predicate<T>(T obj); 从其定义可以看到,此委托引用一个返回bool 值的方法,在实际开发中,通常使用Predicate<T>委托变量引用一个“判断条件函数”,在判断条件函数内部书写代码表明函数参数所引用