再说多线程(三)——Mutex类

news2024/11/20 14:40:57

1.引子

在前面2节,我们已经讨论了Lock语句和Monitor类,Locks 和 Monitors 确保 InProcess 线程的线程安全,即由应用程序本身生成的线程,即内部线程。但是,如果线程来自 OutProcess,即来自外部应用程序,那么 Locks 和 Monitors 将无法控制它们。而 Mutex 确保进程外线程的线程安全,即由外部应用程序生成的线程,即外部线程。

可能前面一段是说的不是很明白,什么是内部进程和外部进程呢?让我们首先创建一个控制台应用程序,然后将以下代码复制并粘贴到其中。

using System;
namespace MutexDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Application Is Running.......");
            Console.ReadKey();
        }
    }
}

编译并运行,找到项目对应的bin文件夹,你会发现一个同名的可执行的exe文件,如图:

如果你运行三遍这个程序,那么就会打开三个实例窗口,因此三个外部线程同事访问着我们的应用程序。

如果你只想让一个程序实例访问内部代码,那么就需要使用Mutex类了,让我们先对上面的代码进行改动:

using System;
using System.Threading;

namespace MutexDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using(Mutex mutex = new Mutex(false, "MutexDemo"))
            {
                //Checking if Other External Thread is Running
                if(!mutex.WaitOne(5000, false))
                {
                    Console.WriteLine("An Instance of the Application is Already Running");
                    Console.ReadKey();
                    return;
                }
                Console.WriteLine("Application Is Running.......");
                Console.ReadKey();
            }
        }
    }
}

再次运行三次这个程序:

可以看出只有第一个实例运行了最后两行,其它实例则无法进入。希望上面的例子能让你理解Mutex的用处。

2.Mutex介绍

Mutex 的工作方式类似于锁,即通过并发访问获得对共享资源的独占锁,但它可以跨多个进程工作。正如我们已经讨论过的,排他锁定基本上用于确保在任何给定时间点,只有一个线程可以进入临界区。

当两个或多个线程需要同时访问一个共享资源时,系统需要一种同步机制来保证一次只有一个线程使用该资源。Mutex 是一种同步机制,它只向一个外部线程授予对共享资源的独占访问权。如果一个线程获取互斥锁,则第二个想要获取该互斥锁的线程将被挂起,直到第一个线程释放该互斥锁。如果目前还不清楚,请不要担心,我会尝试通过一些示例来理解这一点。

2.1 Mutex构造函数

Mutex 是一个密封类,它继承自 WaitHandle 类。因为它是一个密封类,所以不可能有进一步的继承,即在 C# 中不能从这个密封的 Mutex 类派生任何类。

C# 中的 Mutex 类提供了以下五个构造函数,我们可以使用它们来创建 Mutex 类的实例。

  1. Mutex():它使用默认属性初始化 Mutex 类的新实例。

  1. Mutex(bool initiallyOwned):它使用布尔值初始化 Mutex 类的新实例,该值指示调用线程是否应具有互斥锁的初始所有权。

  1. Mutex(bool initiallyOwned, string name):它用一个布尔值初始化 System.Threading.Mutex 类的一个新实例,该值指示调用线程是否应具有互斥锁的初始所有权,以及一个作为互斥锁名称的字符串.

  1. Mutex(bool initiallyOwned, string name, out bool createdNew):它使用布尔值初始化 System.Threading.Mutex 类的新实例,该布尔值指示调用线程是否应具有互斥锁的初始所有权,一个字符串即名称互斥锁的属性,以及一个布尔值,当方法返回时,该值指示调用线程是否被授予互斥锁的初始所有权。

  1. Mutex(bool initiallyOwned, string name, out bool createdNew, MutexSecurity mutexSecurity):它用一个布尔值初始化 System.Threading.Mutex 类的一个新实例,该值指示调用线程是否应该拥有互斥锁的初始所有权,一个字符串是互斥锁的名称,以及一个布尔变量,当方法返回时,它指示调用线程是否被授予互斥锁的初始所有权,以及要应用于指定互斥锁的访问控制安全性。

以下是 C# 中 Mutex 类的构造函数中使用的参数。

  1. initiallyOwned:如果作为调用的结果创建了命名系统互斥锁,则为调用线程提供命名系统互斥锁的初始所有权;否则,假的。

  1. name:互斥量的名称。如果该值为 null,则 Mutex 未命名。

  1. createdNew:当此方法返回时,包含一个布尔值,如果创建了本地互斥锁(即,如果名称为 null 或空字符串)或创建了指定的命名系统互斥锁,则该布尔值为真;如果指定的命名系统互斥量已经存在,则为 false。此参数在未初始化的情况下传递。

  1. mutexSecurity:一个 System.Security.AccessControl.MutexSecurity 对象,表示要应用于指定系统互斥锁的访问控制安全性。

2.2 Mutex成员函数

C# 中的 Mutex 类提供了以下方法。

  1. OpenExisting(string name):此方法用于打开指定的命名互斥量(如果它已存在)。它返回一个代表命名系统互斥锁的对象。此处,参数名称指定要打开的系统互斥锁的名称。如果名称为空字符串,它将抛出 ArgumentException。- 或 - 名称超过 260 个字符。如果名称为空,它将抛出 ArgumentNullException。

  1. OpenExisting(string name, MutexRights rights):此方法用于打开指定的命名互斥锁(如果它已经存在)并具有所需的安全访问权限。它返回一个代表命名系统互斥锁的对象。此处,参数名称指定要打开的系统互斥锁的名称。参数权限指定代表所需安全访问的枚举值的按位组合。

  1. TryOpenExisting(string name, out Mutex result):该方法用于打开指定的命名互斥量,如果它已经存在,并返回一个值,指示操作是否成功。此处,参数名称指定要打开的系统互斥锁的名称。当此方法返回时,结果包含一个 Mutex 对象,如果调用成功,该对象表示指定的互斥锁,如果调用失败,则返回 null。此参数被视为未初始化。如果命名的互斥体被成功打开,它返回真;否则,假的。

  1. TryOpenExisting(string name, MutexRights rights, out Mutex result):此方法用于打开指定的命名互斥锁(如果它已经存在),并具有所需的安全访问权限,并返回一个指示操作是否成功的值。此处,参数名称指定要打开的系统互斥锁的名称。参数权限指定代表所需安全访问的枚举值的按位组合。当此方法返回时,结果包含一个 Mutex 对象,如果调用成功,该对象表示指定的互斥锁,如果调用失败,则返回 null。此参数被视为未初始化。如果命名的互斥体被成功打开,它返回真;否则,假的。

  1. ReleaseMutex():该方法用于释放一次Mutex。

  1. GetAccessControl(): 此方法获取一个 System.Security.AccessControl.MutexSecurity 对象,该对象表示指定互斥体的访问控制安全性。它返回一个 System.Security.AccessControl.MutexSecurity 对象,表示指定互斥锁的访问控制安全性。

  1. SetAccessControl(MutexSecurity mutexSecurity):此方法用于为指定的系统互斥锁设置访问控制安全性。参数 mutexSecurity 指定一个 System.Security.AccessControl.MutexSecurity 对象,该对象表示要应用于指定系统互斥锁的访问控制安全性。

3.举例分析

直接看下面的例子,关键地方给了注释。

internal class Example1
    {
        private static Mutex mutex = new Mutex();
        public static void Run()
        {
            //Create multiple threads to understand Mutex
            for (int i = 1; i <= 5; i++)
            {
                Thread threadObject = new Thread(MutexDemo)
                {
                    Name = "Thread " + i
                };
                threadObject.Start();
            }
            Console.ReadKey();
        }
        static void MutexDemo()
        {
            Console.WriteLine(Thread.CurrentThread.Name + " Wants to Enter Critical Section for processing");
            try
            {
                //Blocks the current thread until the current WaitOne method receives a signal.  
                //Wait until it is safe to enter. 
                mutex.WaitOne();
                Console.WriteLine("Success: " + Thread.CurrentThread.Name + " is Processing now");
                Thread.Sleep(2000);
                Console.WriteLine("Exit: " + Thread.CurrentThread.Name + " is Completed its task");
            }
            finally
            {
                //Call the ReleaseMutex method to unblock so that other threads
                //that are trying to gain ownership of the mutex can enter  
                mutex.ReleaseMutex();
            }
        }
    }

因为每个线程都会被阻塞直到拥有mutex的所有权,在运行结束后,必须调用ReleaseMutex释放所有权,以保证其它线程可以进入。运行结果如下:

Thread 2 Wants to Enter Critical Section for processing
Thread 1 Wants to Enter Critical Section for processing
Thread 3 Wants to Enter Critical Section for processing
Thread 4 Wants to Enter Critical Section for processing
Thread 5 Wants to Enter Critical Section for processing
Success: Thread 2 is Processing now
Exit: Thread 2 is Completed its task
Success: Thread 1 is Processing now
Exit: Thread 1 is Completed its task
Success: Thread 3 is Processing now
Exit: Thread 3 is Completed its task
Success: Thread 4 is Processing now
Exit: Thread 4 is Completed its task
Success: Thread 5 is Processing now
Exit: Thread 5 is Completed its task

当然,也可以给WaitOne加一个期限,WaitOne(Int32),如果超过规定时间都没有获取到mutex,那么将返回false,且不会再尝试获取mutex。看下面的例子:

 class Program
    {
        private static Mutex mutex = new Mutex();
        static void Main(string[] args)
        {
            //Create multiple threads to understand Mutex
            for (int i = 1; i <= 3; i++)
            {
                Thread threadObject = new Thread(MutexDemo)
                {
                    Name = "Thread " + i
                };
                threadObject.Start();
            }
            Console.ReadKey();
        }
        //Method to implement syncronization using Mutex  
        static void MutexDemo()
        {
            // Wait until it is safe to enter, and do not enter if the request times out.
            Console.WriteLine(Thread.CurrentThread.Name + " Wants to Enter Critical Section for processing");
            if (mutex.WaitOne(1000))
            {
                try
                {
                    Console.WriteLine("Success: " + Thread.CurrentThread.Name + " is Processing now");
                    Thread.Sleep(2000);
                    Console.WriteLine("Exit: " + Thread.CurrentThread.Name + " is Completed its task");
                }
                finally
                {
                    //Call the ReleaseMutex method to unblock so that other threads
                    //that are trying to gain ownership of the mutex can enter  
                    mutex.ReleaseMutex();
                    Console.WriteLine(Thread.CurrentThread.Name + " Has Released the mutex");
                }
            }
            else
            {
                Console.WriteLine(Thread.CurrentThread.Name + " will not acquire the mutex");
            }
        }
        ~Program()
        {
            mutex.Dispose();
        }
    }

当然,为了控制实例程序同事访问,我们可以使用OpenExist函数来进行判断,其定义如下:

public static System.Threading.Mutex OpenExisting (string name);

该函数会试图获取指定名称的mutex对象,如果存在则返回该对象,如果不存在则抛出异常,因此我们可以在程序第一个实例时创建mutex对象,当后面启动新的实例后,判断已经存在,那么就阻止实例启动。例子如下:

internal class SingleInstance
    {
        static Mutex? _mutex;
        public static void Run()
        {
            if(!IsSingleInstance())
            {
                Console.WriteLine("More than one instance");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                return;
            }
            else
            {
                Console.WriteLine("One instance");
            }
            Console.ReadLine();
        }

        static bool IsSingleInstance()
        {
            try
            {
                Mutex.OpenExisting("MyApp");
            }
            catch
            {
                _mutex=new Mutex(true,"MyApp");
                //Only one instance
                return true;
            }
            //more than one Instance.
            return false;
        }
    }

连续运行该程序两次,会发现第二次运行后,实例在2秒自动关闭。


虽然Mutex可以解决程序多启动问题,确保外部线程只有一个可以访问,但是如果要精确控制外部线程数量,Mutex就无能为力了,这就需要用到下一节讲的Semaphore类了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162941.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 诊断利器 Arthas monitor/watch/trace命令

一、监控相关命令介绍 二、监控相关命令 2.1、运行Demo 2.2、monitor 命令 2.2.1、方法监控 2.3、watch 命令 &#xff08;重要&#xff09; 2.3.1、观察函数调用返回时的参数、this 对象和返回值 2.3.2、查看函数调用的入参和返回值 2.3.3、深度遍历 x 说明 2.3.4、查…

检验仪器控制怎么停止的

之前介绍仪器控制启动是按维护的调用M和仪器ID组串直接j启动进程&#xff0c;进程在调用Start启动TCP。 组装执行M串用j启动进程 启动TCP通道&#xff0c;成功之后就到f的死循环了 死循环这里容易有个误解&#xff0c;以为Stop是停止仪器接口的。其实这个Stop是判断要不要…

一灯大师,基于imx6ull点亮LED灯

一.imx6ull GPIO原理1. STM32 GPIO回顾我们一般拿到一款全新的芯片&#xff0c;第一个要做的事情的就是驱动其 GPIO&#xff0c;控制其 GPIO 输出高低电平&#xff0c;我们学习 I.MX6U 也一样的&#xff0c;先来学习一下 I.MX6U 的 GPIO。在学习 I.MX6U的 GPIO 之前&#xff0c…

Spark WordCount 案例

文章目录Spark WordCount 案例1、程序连接 Spark2、WordCount 案例示例3、复杂版 WordCount4、Spark 框架WordcountSpark WordCount 案例 1、程序连接 Spark 首先这个Scala spark程序和spark的链接&#xff0c;跟sql编程类似。首先new 一个新的val context SparkContext()对…

谷粒商城-高级篇-Day10-ElasticSearch

初步检索 1、_cat GET /_cat/nodes:查看所有节点 GET/_cat/health:查看es健康状况 GET/_cat/master:查看主节点 GET/_cat/indices:查看所有索引–相当于查询所有数据库 2、索引一个文档 put&#xff1a;http://192.168.205.128:9200/customer/external/1 {"name&qu…

Qt之加载百度离线地图(WebKit和WebEngine)

最近翻看进年前写了一篇关于百度离线地图的博客:Qt加载百度离线地图,发现存在很多问题,比如不能加载折线等图形覆盖物;只支持QtWebKit,不支持QtWebEngine。 之前做项目需要在百度离线地图上绘制Mesh拓扑图,必须添加折线覆盖物,使用的是百度离线地图API V2.1,满足需求。…

Java注解详解

什么是注解 ​ 用一个词就可以描述注解&#xff0c;那就是元数据&#xff0c;即一种描述数据的数据。所以&#xff0c;可以说注解就是源代码的元数据 元注解 JDK1.5之后内部提供的注解&#xff1a; Deprecated 意思是“废弃的&#xff0c;过时的”Override 意思是“重写、覆…

算法训练营 day18 二叉树 找树左下角的值 路径总和 从中序与后序遍历构建二叉树

算法训练营 day18 二叉树 找树左下角的值 路径总和 从中序与后序遍历构建二叉树 找树的左下角 513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节…

Java --- JUC之原子类

目录​​​​​​​ 一、基本类型原子类 二、数组类型原子类 三、引用类型原子类 四、对象的属性修改类型原子类 五、原子操作增强类 5.1、高性能热点商品应用 5.2、LongAdder架构图 5.3、源码分析 一、基本类型原子类 public class AtomicTest1 {public static final…

canvas:基础知识【直线和矩形】

canvas&#xff0c;就是画布&#xff0c;是HTML5和核心技术之一&#xff0c;结合JavaScript&#xff0c;可以绘制各种各样的图形&#xff0c;比如矩形、曲线、圆形等等。另外&#xff0c;canvas可以绘制图表、动画效果、游戏开发。 基本图形汇中有直线和曲线。常见的直线图形是…

arduino rc522模块使用

rfid IC卡 先了解IC卡一些前置知识。 首先我们会有一张ic卡&#xff08;M1类型IC卡&#xff0c;一般买到的都是1K存储空间&#xff09;&#xff0c;在rc522代码中会出现这个&#xff0c;就是对IC卡进行检查PICC_TYPE_MIFARE_4K和PICC_TYPE_MIFARE_1K就是一种卡片类型不同大小…

零基础学MySQL(二)-- 表的创建,修改,删除

文章目录&#x1f388;一、创建表1️⃣基本语法2️⃣入门案例&#x1f386;二、MySQL常用数据类型1️⃣数值型&#xff08;整型&#xff09;默认有符号2️⃣数值型&#xff08;bit&#xff09;3️⃣数值型&#xff08;浮点型&#xff09;默认有符号4️⃣字符串的基本使用5️⃣字…

1584_AURIX_TC275_SMU的调试以及部分寄存器

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 前面学习的过程中&#xff0c;突然间减速了不少。但是为了保证学习的推进&#xff0c;还是得有每天的稳定输出。我的策略是多看&#xff0c;多处理&#xff0c;之后每天整理10页标注的文档…

设计模式相关内容介绍

1.学习设计模式好处 提高编程能力、思维能力、设计能力程序设计更加标准化、代码编制更加工程化&#xff0c;软件开发效率大大提高&#xff0c;缩短项目周期设计的代码可重用性高、可读性强、可靠性高、 灵活性好、可维护性强 2.设计模式分类 创建型模式 提供创建对象的机制…

一文读懂工业级交换机的规范使用

工业交换机具备电信级特性特点&#xff0c;可承受严苛的工作环境&#xff0c;产品种类丰富多彩&#xff0c;交换机配置灵便&#xff0c;可以满足各类工业应用的应用标准。那么&#xff0c;大家使用工业级交换机的过程当中应该如何规范使用呢&#xff1f; 工业级交换机其实质运…

蓝队攻击的四个阶段(四)

目录 一&#xff0c; 外网纵向突破 1.1 何为外网纵向突破 1.2外网纵向突破的主要工作 二&#xff0c; 外网纵向突破的途径 1. Web 网站 2.外部邮件系统 3.边界网络设备 4.外部应用平台 三&#xff0c;内网横向拓展 1. 1何为内网横向拓展 1.2 内网横向拓展的主要工作 …

电商价格监测,关注这些,才算实际到手价

品牌控价的第一项工作&#xff0c;是先找出低价乱价链接&#xff0c;这就需要进行电商价格监测。但是我们搜索品牌链接的时候&#xff0c;会发现网页上的价格是多种多样&#xff1a;有原价&#xff08;但是划掉了&#xff09;、促销价、折扣价、惊喜价&#xff0c;优惠活动也是…

localStorage

localStorage localStorage了解 有些数据确实需要存储在本地&#xff0c;但是它却不需要发送到服务器&#xff0c;所以并不适合放在cookie中 localStorage 也是一种浏览器存储数据的方式&#xff08;本地存储&#xff09;&#xff0c;它只是存储在本地&#xff0c;不会发送…

【Linux】进程间通信(1)

信号 什么是信号&#xff1f;信号是给程序提供一种可以处理异步事件的方法&#xff0c;它利用软件中断来实现。不能自定义信号&#xff0c;所有信号都是系统预定义的。 信号由谁产生&#xff1f; 由shell终端根据当前发生的错误&#xff08;段错误、非法指令等&#xff09;Ctr…

商品详情的APP原数据接口测试

一、原数据接口的来源&#xff1a; 原数据接口来源于手机端&#xff0c;随着智能化的发展与普及&#xff0c;越来越多的人都是使用智能手机&#xff0c;这样极大的方便了人民的生活&#xff0c;各大电商平台看准了这个商家&#xff0c;把目光都瞄准这个商机&#xff0c;伴随而…