不过这热气是从实在的火里发出来的呢,还是从他的爱情里发出来的呢,他完全不知道。他的一切光彩现在都没有了。这是因为他在旅途中失去了呢,还是悲愁的结果,谁也说不出来。
                                                                                    ——安徒生
                                                                                          摘自《坚定的锡兵》
摘要

1-2-3翻开那《葵花宝典》,只见页首赫然写着几个大字:“欲练神功,必先自宫”,旁边几行歪歪扭扭的小字,又不知是哪位前辈高人所写:“在WC里占蹲位的3种方法:1. 如果你只对某个蹲位情有独钟,就要WaitOne(),但是不要忘了ReleaseMutex(),千万别WaitOne()两次只ReleaseMutex()一次(你干这种占着MK不LS的事,憋坏了后来的小朋友怎么办?就算没有小朋友,憋坏了小猫小狗也不好啊……);2. 如果你喜欢讲排场,需要占2个蹲位才肯办事,则要WaitAll([蹲位1, 蹲位2]);3. 如果你觉得随便去哪个蹲位办事都无所谓,那就可以WaitAny([蹲位1, 蹲位2])……”。

Mutex的WaitOne()函数

前几天1-2-3去黑木崖找东方不败玩,听到东方不败抱怨说整天绣花眼睛好累呀,于是1-2-3就给东方不败编了一个活动眼睛的程序。

class Program
{
    static void Main(string[] args)
    {
        // 为截图方便把窗体设小一点
        Console.WindowWidth = 30; Console.BufferWidth = 30; 
        Console.WindowHeight = 16; Console.BufferHeight = 16;
        
        Mutex mk = new Mutex(false, "my mutex");
        for (int i = 0; i < 1000; i++)
        {
            mk.WaitOne();
            for (int j = 0; j < 30; j++)
            {
                Console.Write(">");
                Thread.Sleep(100);
            }
            mk.ReleaseMutex();
            Thread.Sleep(500);
        }
    }
}

接连运行此程序的两个实例,把它们并排排放在一起(如下图所示),即可看到箭头从左边的窗体“穿越”到右边窗体的效果了。

是的,我们需要同步两个进程(中的主线程),这个工作需要交给Mutex。Mutex和Monitor的概念十分相似,只不过Monitor是.net内建的线程同步机制,Mutex是封装了Windows操作系统的线程同步机制;Monitor速度快,Mutex的速度要比Monitor慢很多;Monitor只能用于同步同一进程内的线程;Mutex则可以用于同步隶属于不同进程的线程。

Mutex的WaitAll()函数

现在我们对WC进行了扩建,把mk增加到两个,可是却遇到了两个讲排场的进程,它们都要同时占两个mk才肯办事,所以运行起来的效果和前一个程序一样。

class Program
{
    static void Main(string[] args)
    {
        // 为截图方便把窗体设小一点
        Console.WindowWidth = 30; Console.BufferWidth = 30; 
        Console.WindowHeight = 16; Console.BufferHeight = 16;
        
        Mutex mk1 = new Mutex(false, "my mutex1");
        Mutex mk2 = new Mutex(false, "my mutex2");
        Mutex[] mks = new Mutex[] { mk1, mk2 };

for (int i = 0; i < 1000; i++)
        {
            Mutex.WaitAll(mks);
            for (int j = 0; j < 30; j++)
            {
                Console.Write(">");
                Thread.Sleep(100);
            }

mk1.ReleaseMutex();
            mk2.ReleaseMutex();
            Thread.Sleep(500);
        }
    }
}

Mutex的WaitAny()函数

看下这个小程序

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         // 为截图方便把窗体设小一点
 6         Console.WindowWidth = 30; Console.BufferWidth = 30; 
 7         Console.WindowHeight = 16; Console.BufferHeight = 16;
 8         
 9         Mutex mk1 = new Mutex(false, "my mutex1");
10         Mutex mk2 = new Mutex(false, "my mutex2");
11         Mutex[] mks = new Mutex[] { mk1, mk2 };
12 
13         for (int i = 0; i < 1000; i++)
14         {
15             int index = Mutex.WaitAny(mks); // 返回值为此进程占用的mk在mks里的index
16             Console.Write("Index: " + index.ToString());
17             for (int j = 0; j < 30; j++)
18             {
19                 Console.Write(">");
20                 Thread.Sleep(100);
21             }
22 
23             mks[index].ReleaseMutex();
24             Thread.Sleep(new Random().Next(100, 3000));
25         }
26     }
27 }

如果同时运行此程序的两个实例,正如本文摘要里所写的,只要mk1和mk2有一个是空闲的,进程就可以进去办事,所以两个进程可以同时输出">"字符。注意程序的第24行,每个进程在输出30个">"字符后都会随机Sleep 100到3000毫秒,这样就有可能出现mk1和mk2同时空闲的情况,所以就会出现一会儿进程1占用mk1而进程2占用mk2;一会儿进程1占用mk2而进程2占用mk1的情况(在下图分别用绿色和红色波浪线标出)。

EventWaitHandle、AutoResetEvent 和 ManualResetEvent

EventWaitHandle的名字与Mutex差了很多,不过它可是Mutex不折不扣的兄弟——它和Mutex都是WaitHandle的子类,用法也差不多。下面这两段程序实现了与本文的第一段程序相同的功能。

1) AutoReset
class Program
{
    static void Main(string[] args)
    {
        // 为截图方便把窗体设小一点
        Console.WindowWidth = 30; Console.BufferWidth = 30;
        Console.WindowHeight = 16; Console.BufferHeight = 16;

        EventWaitHandle mk = new EventWaitHandle(true, EventResetMode.AutoReset, "my mk");
        
        for (int i = 0; i < 1000; i++)
        {
            mk.WaitOne();
            for (int j = 0; j < 30; j++)
            {
                Console.Write(">");
                Thread.Sleep(100);
            }
            mk.Set();
            Thread.Sleep(500);
        }
    }
}
2) ManualReset
class Program
{
    static void Main(string[] args)
    {
        // 为截图方便把窗体设小一点
        Console.WindowWidth = 30; Console.BufferWidth = 30;
        Console.WindowHeight = 16; Console.BufferHeight = 16;

        EventWaitHandle mk = new EventWaitHandle(true, EventResetMode.ManualReset, "my mk");
        
        for (int i = 0; i < 1000; i++)
        {
            mk.WaitOne();
            mk.Reset();   // 把这行去掉会咋样?
            for (int j = 0; j < 30; j++)
            {
                Console.Write(">");
                Thread.Sleep(100);
            }
            mk.Set();
            Thread.Sleep(500);
        }
    }
}

AutoResetEvent 和 ManualResetEvent 是 EventWaitHandle 的子类,功能都差不多,就不多说了。

(本系列完)

白话并发冲突与线程同步(3)——Mutex、EventWaitHandle、AutoResetEvent 和 ManualResetEvent...相关推荐

  1. C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

    C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent 本章概要: 1:终止状态和非终止状态 2:AutoResetEvent和ManualResetE ...

  2. Java核心(三)并发中的线程同步与锁

    2019独角兽企业重金招聘Python工程师标准>>> 乐观锁.悲观锁.公平锁.自旋锁.偏向锁.轻量级锁.重量级锁.锁膨胀...难理解?不存的!来,话不多说,带你飙车. 上一篇介绍了 ...

  3. JAVA并发编程3_线程同步之synchronized关键字

    在上一篇博客里讲解了JAVA的线程的内存模型,见:JAVA并发编程2_线程安全&内存模型,接着上一篇提到的问题解决多线程共享资源的情况下的线程安全问题. 不安全线程分析 public clas ...

  4. 对Java多线程编程的初步了解,实现多线程的三种方式以及多线程并发安全的线程同步机制

    什么叫进程?什么叫线程? 进程相当于一个应用程序,线程就是进程中的一个应用场景或者说是一个执行单元,一个进程可以启动多个线程,每个线程执行不同的任务,一个线程不能单独存在,他必须是进程的一部分,当进程 ...

  5. Java并发编程之线程同步

    线程安全就是防止某个对象或者值在多个线程中被修改而导致的数据不一致问题,因此我们就需要通过同步机制保证在同一时刻只有一个线程能够访问到该对象或数据,修改数据完毕之后,再将最新数据同步到主存中,使得其他 ...

  6. java线程并发库之--线程同步工具CountDownLatch用法

    CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 主要方法 public CountDownLatch(int count); pu ...

  7. Android中线程同步之Mutex与Condtion的用法

    简单的线程与锁关系 基本概念 http://blog.csdn.net/thl789/article/details/9879151 数据不一致现象 http://blog.sina.com.cn/s ...

  8. java线程并发库之--线程同步工具Exchanger的使用

    Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据.今天我们就通过实例来学习一下Exchanger的用法. Exchanger的简单实例 Exchanger是 ...

  9. 【转】1.3异步编程:线程同步基元对象

    开始<异步编程:同步基元对象(上)> 示例:异步编程:线程同步基元对象.rar 如今的应用程序越来越复杂,我们常常需要多线程技术来提高我们应用程序的响应速度.每个线程都由自己的线程ID,当 ...

  10. 9 C++ Boost 多线程,线程同步

    线程的创建 boost_thread,boost_system 多线程的创建 线程的参数传递 线程的创建方式 线程的join 加入join,回收线程线程中断 线程中断2, 线程组 boost 线程的死 ...

最新文章

  1. 新都一职高计算机学什么,新都第一职业高中怎么样
  2. 计算机领域认知个人陈述,计算机专业个人陈述十九
  3. 计算机word考试中的图文混排,2014招警考试公共基础计算机知识:Word的图文混排功能...
  4. UVA - 11437 Triangle Fun(简单几何)
  5. [Leedcode][JAVA][第1014题][最佳观光组合][数组][暴力优化]
  6. Java面试高频题:Spring Boot+Sentinel+Nacos高并发已撸完
  7. HttpURLConnection类的使用
  8. Oracle listener lsnrctl
  9. IE下Ajax缓存(转载)
  10. ArcGIs创建企业级数据库
  11. 学习.NET好书推荐
  12. 中专计算机英语听课记录,【听课记录15篇及评析】_英语听课记录及评析_英语听课记录及评析范文5篇...
  13. 关于 Cannot assign requested address 错误
  14. Chrome漏洞分析与利用(三)——Issue-1062091漏洞分析
  15. Nature Genetics:华中农业大学严建兵团队合作揭示玉米和玉米的“父辈”如何适应环境进化...
  16. 三维重建技术(2)各种方法简介
  17. stream对象集合按照时间进行排序
  18. MK趋势检验和MK突变检验(代码分享及结果分析)
  19. 心理学之舒适区、学习区、恐慌区
  20. xp系统怎么启动任务计划服务器,windowsXP操作系统如何设置计划任务

热门文章

  1. Mac新手教程:Adobe Illustrator 如何创建文件
  2. Vegas的新功能——触摸编辑
  3. iOS开发之结构体底层探索
  4. 常用网页元素命名规范参考
  5. Jersey 2.x 运行项目
  6. Netty NioEventLoop 启动过程源码分析
  7. c#命名规范(转载)
  8. php+mysql+json android 连接wamp
  9. PHP网站实现地址URL重定向
  10. 常见的SQL错误和解决方法