互斥体与互锁 <第五篇>

互斥体与互锁 <第五篇>

  互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”。因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。

  任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。

1、Mutex

  Mutex是一个同步基元,它与前面提到的锁最大的区别在于它支持进程间同步。

  Mutex允许同一个线程多次重复访问共享区,但是对于别的线程那就必须等待,它甚至支持不同进程中的线程同步,这点更能体现他的优势,但是劣势也是显而易见的,那就是巨大的性能损耗和容易产生死锁的困扰,所以除非需要在特殊场合,否则 我们尽量少用为妙,这里并非是将Mutex的缺点说的很严重,而是建议大家在适当的场合使用更为适合的同步方式,Mutex 就好比一个重量型的工具,利用它则必须付出性能的代价。

  1、Mutex线程同步

  Mutex实现线程同步主要依靠以下两个方法实现:

  • WaitOne 阻止当前线程,直到当前 WaitHandle 收到信号。
  • ReleaseMutex 释放 Mutex 一次。
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                ThreadPool.QueueUserWorkItem(Run);
            }
            Console.ReadKey();
        }

        static int count = 0;
        static Mutex mutex = new Mutex();
        static void Run(object obj)
        {
            //阻止当前线程(如果去掉这两行)
            mutex.WaitOne(); 
            Console.WriteLine("当前数字:{0}", ++count);
            //释放 Mutex (如果去掉这行)
            mutex.ReleaseMutex();
        }
    }

  输出:

  互斥体与互锁 <第五篇>

  如果去掉则输出如下:

  互斥体与互锁 <第五篇>

  Mutex和Monitor的区别:

  1. Monitor不是waitHandle的子类,它具有等待和就绪队列的实际应用;
  2. Monitor无法跨进程中实现线程同步,但是Mutex可以;
  3. 相对而言两者有明显的性能差距,mutex相对性能较弱但是功能更为强大,monitor则性能比较好;
  4. 两者都是用锁的概念来实现同步不同的是monitor一般在方法(函数)调用方加锁;mutex一般在方法(函数)内部加锁,即锁定被调用端;
  5. Monitor和Lock多用于锁定被调用端,而Mutex则多用锁定调用端。

  2、进程间同步

  当给Mutex取名的时候能够实现进程同步,不取名实现线程同步。

  Mutex有两种类型:未命名的局部mutex和已命名的系统mutex。

  • 本地mutex仅存在与进程当中,进程内可见;
  • 已命名的系统mutex在整个操作系统中可见,可用于同步进程活动;
    class Program
    {
        static void Main(string[] args)
        {
            //使用线程输出等待状态
            Thread t1 = new Thread(ShowMyWord);
            t1.Start();

            Run(t1);

            Console.Read();
        }

        static int count = 0;
        static Mutex mutex = new Mutex(false, "xxoo"); static void Run(Thread t1)
        {
            //这个WaitOne方法要么返回true,要么一直不返回(不会返回false),所以没办法用if来判断
            //于是,用个线程输出等待状态
            mutex.WaitOne();
            Console.WriteLine("终于轮到老子了!  " + DateTime.Now.TimeOfDay.ToString());
            //停止线程t1,不要再输出等待状态
            t1.Abort();
            //模拟干活十秒
            Thread.Sleep(10000);
            Console.WriteLine("干完!  " + DateTime.Now.TimeOfDay.ToString());
            //释放 Mutex
            mutex.ReleaseMutex();
        }

        static void ShowMyWord(object obj)
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(2000);
                Console.WriteLine("我的心在等待,一直在等待!   " + DateTime.Now.TimeOfDay.ToString());
            }
        }
    }

  以上代码,将生成的.exe文件复制两份:

  互斥体与互锁 <第五篇>

  快速运行两个输出如下:

  互斥体与互锁 <第五篇>

  3、互斥体控制控制台程序仅能启动一次

  由于Mutex能够用于进程间同步,因此我们可以很轻易地利用它实现控制程序只能启动一次的效果。

    static void Main(string[] args)
    {
        bool IsFirstCreate;
        Mutex instance = new Mutex(true, "NewApplication", out IsFirstCreate);
        if (IsFirstCreate) //赋予了线程初始所属权,也就是首次使用互斥体 
        {
            instance.ReleaseMutex();
        }
        else
        {
            Console.WriteLine("你已经启动了一个程序,本程序将于5秒后自动退出!");
            Thread.Sleep(5000);
            return;
        }
        Console.WriteLine("程序启动成功!");
        Console.ReadKey();
    }

  输出如下:

  互斥体与互锁 <第五篇>

  4、控制WinForm程序只能启动一次

static class Program
{
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()
    {
        bool IsFirstRun;
        Mutex instance = new Mutex(true, "xxx", out IsFirstRun);
        if (IsFirstRun) //赋予了线程初始所属权,也就是首次使用互斥体 
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            instance.ReleaseMutex();
        }
        else
        {
            MessageBox.Show("你已启动了一个程序,本程序将于5秒后退出", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            Thread.Sleep(5000);
            Application.Exit();
        }
    }
}

  输出如下:

   互斥体与互锁 <第五篇>

  第3、4测试均需要在bin目录下直接启动多个.exe。

二、Interlocked

   实际引用中,可能我们对共享变量的使用并不十分复杂,可能只是一些简单的操作如:自增、自减、求和、赋值、比较等。

   MSDN中的解析Interlocked为多个线程共享的变量提供原子操作。

  常用操作如下:

方法 说明
Add 相加
CompareExchange 比较
Increment 递增
Decrement 递减
Exchange 赋值

   示例:

   class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 20; i++)
            {
                Thread t = new Thread(Run);
                t.Start();
            }

            Console.Read();
        }

        static int Incre = 0;
        static int Add = 0;
        static int Exchange = 0;
        static int Decre = 21;
        static int CompareExchange = 0;

        static Mutex mutex = new Mutex();

        static void Run()
        {
            //自增操作
            //Console.WriteLine("当前数字:{0}", Interlocked.Increment(ref Incre));
            //递减操作
            //Console.WriteLine("当前数字:{0}", Interlocked.Decrement(ref Decre));
            //相加
            //Console.WriteLine("当前数字:{0}", Interlocked.Add(ref Add,10));
            //赋值
            //Console.WriteLine("当前数字:{0}", Interlocked.Exchange(ref Exchange, 5));
            //比较,如果第三个参数等于CompareExchange,则将第二个参数的值,赋给第一个参数
            Console.WriteLine("当前数字:{0}", Interlocked.CompareExchange(ref CompareExchange, 15,0));
            
        }
    }

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/110131.html原文链接:https://javaforall.net

(0)
上一篇 2021年8月23日 下午9:00
下一篇 2021年8月23日 下午10:00


相关推荐

  • Python 查看数据类型与格式

    Python 查看数据类型与格式一般我们拿到一个数据 会先看一下这个数据有多少行多少列 各个字段是什么 数据格式类型是什么 在开始讲数据格式前 需要先梳理一下各个数据类型 我们常使用的库一般是 numpy 和 pandas Numpy 下的核心是数组 array ndarray Pandas 下的核心是数据框 Series DataFrame 先随机创一点数据用来测试 importpandas

    2026年3月16日
    2
  • html表格空格符是什么,HTML中的空格符号是什么

    html表格空格符是什么,HTML中的空格符号是什么在HTML中的空格符号有:表示不断行的空白; 表示半个空白; 表示一个空白等在平时我们写文章时遇到空格时都会直接输入空格键来达到空格的效果,但是在HTML代码中如果我们输入空格键就会被忽略,达不到空格的效果,今天就像大家介绍HTML中空格代码如何写,希望对大家有所帮助。【推荐课程:HTML课程】方法是HTML空格转义字符,当我们需要几个空白字符时就输入几个例Gxl网提供大量免费、原创、…

    2022年10月4日
    4
  • 网络安全学习查询黑客

    网络安全学习查询黑客网络安全查询学习密码泄露查询http://cha.xiyv.nethttp://www.90geek.com/sed/(不错)http://www.90geek.com/(不错)http://9cha8.cn/index.aspxhttp://www.90geek.com/post(不错)http://lucky.anquanbao.com/(信息模糊)https://haveibeenpwned…

    2022年10月5日
    3
  • Caffe 与 Caffe2

    Caffe 与 Caffe2Caffe 与 Caffe2

    2026年3月17日
    2
  • java三元运算符怎么用_按位运算符

    java三元运算符怎么用_按位运算符Java提供了一个三元运算符,可以同时操作3个表达式。三元运算符语法格式如下:判断条件?表达式1:表达式2在上述语法格式中,当判断条件成立时,计算表达式1的值作为整个表达式的结果,否则计算表达式2的值作为整个表达式的结果。三元运算符的功能与if…else语法相同,但是使用三元运算符可以简化代码。例如,求两个数x、y中的较大者,如果用if.else语句来实现,具体代码如下:Intx=0;inty=1;intmax;if(x>y){max=x;}el

    2022年10月20日
    7
  • java 表头固定_常用的固定表头的几种做法

    java 表头固定_常用的固定表头的几种做法这几天修改列表的样式风格 提到了列表表头的固定方法 总结一下常见的几种 第一种感觉最简单的就是分放在两个标签里 这样只控制不含有标题的那个 代码如下 标题一标题二标题三标题四 4

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号