Form1单例模式与互斥锁

一、使用mutex来解决。

    如何让窗体Form1也是一个单例模式呢?
    在窗体项目中找到Program.cs,双击。找到入口点,更改如下:

    [STAThread]private static void Main(){string mutexName = "MyapplicatonMutexApp1121";using (Mutex m = new Mutex(true, mutexName, out bool isFist)){if (isFist == false){MessageBox.Show("已经在运行了.");return;}Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}


        上述代码是一个C#控制台应用程序的入口方法Main,它使用了STAThread属性和互斥锁来
    控制应用程序的运行。
        首先,定义了一个字符串变量mutexName来表示互斥锁的名称。互斥锁是一种同步机制,
    用于在多个线程之间实现资源的互斥访问,确保同一时间只有一个线程可以访问被保护的资
    源。
        接下来,使用using语句以编程方式创建一个互斥锁Mutex。Mutex构造函数的第一个参数
    为true,表示创建一个初始状态为"有信号"的互斥锁。第二个参数是互斥锁的名称,用于唯一
    标识这个互斥锁。第三个参数out bool isFirst是一个out参数,用于返回一个布尔值,指示
    是否是第一个线程获得了互斥锁。
        接下来,通过检查isFirst的值来判断是否是第一个获得互斥锁的线程。如果isFirst的
    值为false,则说明已经有另一个实例正在运行,弹出一个消息框提示用户已经在运行,并
    直接退出应用程序;如果isFirst的值为true,则说明当前是第一个获得互斥锁的线程,继
    续执行应用程序。
    
    当应用程序第一次运行时,它将获得互斥锁,然后调用Application.EnableVisualStyles()和
    Application.SetCompatibleTextRenderingDefault(false)方法来启用可视化样式和设置文
    本呈现默认值。最后,调用Application.Run(new Form1())运行主窗体Form1,启动应用程序
    的消息循环,处理用户界面和事件。
        因此,上面保证只有一个实例运行,通过互斥锁来控制应用程序的运行。当多个实例尝
    试运行时,只有第一个实例能够成功获得互斥锁并继续执行,其他实例将被提示已经在运行,
    并退出应用程序。这种方法通常用于单实例应用程序,以确保不会同时启动多个相同的应用
    程序实例。
    
    
    问:上面[STAThread]有什么用?
    答:[STAThread]是一个属性,用于指定应用程序的线程模型。上面应用于Main方法,用于确
    保应用程序在单线程单元(STA)模式下执行。
        在多线程应用程序中,STA模式是一种线程模型,它要求应用程序的所有线程都在单个单
    元中执行,并且每个线程都与消息循环相关联。这对于许多GUI框架和组件是必需的,因为它
    们通常依赖于在单个线程中进行消息处理。
        [STAThread]属性告诉应用程序使用STA模式运行。该属性可以应用于整个应用程序(在
    入口点Main方法上)或特定线程。确保应用程序以STA模式运行对于使用Windows Forms、WPF
    或COM组件的应用程序是必要的。
        如果应用程序需要与其他线程进行互操作,可能需要考虑使用不同的线程模型,如多线
    程单元(MTA)模式。但在大多数情况下,使用[STAThread]来运行应用程序是常见和推荐的做
    法。
        STA(单线程单元)模式可以实现按顺序执行,减少多线程竞争资源的问题,并且能够
    提供一致和可预测的程序执行顺序。但在某些情况下,例如需要处理大量计算密集型任务或
    需要与其他线程进行并行操作的场景,MTA(多线程单元)模式可能更适合。在这种情况下,
    需要仔细考虑线程同步和资源竞争问题,并采取适当的并发控制措施。
    
    问:Mutex m = new Mutex(true, mutexName, out bool isFist)是什么意思?
    答:此句用Mutex 类来创建一个互斥锁,并指定了参数。m 是一个 Mutex 类型的变量,用于
    表示创建的互斥锁。
        new Mutex(true, mutexName, out bool isFist) 是创建 Mutex 实例的构造函数调用:
        true 表示在创建互斥锁时设置一个初始状态,即锁定状态。(上锁)
        mutexName 是一个字符串参数,用于指定互斥锁的名称。
        out bool isFist 是一个输出参数,用于指示是否是第一次创建互斥锁。
        因此,这段代码的功能是创建一个名为 mutexName 的互斥锁,并在创建时将其锁定。
    同时,通过 out bool isFist 参数返回一个布尔值,指示是否是第一次创建该互斥锁。
        第一个参数 true 表示在创建互斥锁时将其初始化为锁定状态。这意味着在调用
    new Mutex(true, mutexName, out bool isFist) 时,如果其他线程已经锁定了该互斥
    锁,则创建过程将会失败,isFist 参数将会被设置为 false。
        如果创建成功,则 isFist 参数将会被设置为 true,表示当前线程是第一个锁定
    该互斥锁的线程。
        如果第一个参数为 false,则无论其他线程是否已经锁定了该互斥锁,isFist 参数
    都会被设置为 false。

         

        通俗解释:mutex的Name属性很重要,它是识别mutex锁的重要标志。可以命名不同的锁,尽量长而具有唯一识别性,第二次来判断这个mutex锁就是根据这个Name。当然你也可以用唯一GUID来识别,但第二次运行时会重新生成另一个GUID,肯定与第一次的不同,除非,你把GUID保存在硬盘日志上,下次运行时,先读取硬盘从而取出GUID。为了简省,采取较长且多样字符的来命名Name,省去读日志文件的麻烦。

       m = new Mutex(true, mutexName, out bool isFist)这一句就是上锁,设置初始拥有true,因此,如果上锁成功isFirst为true,说明之前没有实例进行上锁,没有人拥有这个锁。这个时候就可以运行实例。但如果现在上锁失败,说明已经被别人拥有,别人已经锁上了,自己进不了,也就是已经有实例在运行了,所以第二次运行只有退出,从而保证只有一实例。


二、理解Mutex


    1、当我们想要确保多个线程同时访问某个共享资源时不会产生冲突时,可以使用Mutex类。
        你可以把Mutex类看作是一个特殊的锁,它可以帮助我们控制对共享资源的访问。
            你可以将Mutex类比作一间只能容纳一人的厕所。每当一个人想要使用厕所时,他必
        须先查看门口的指示灯。如果指示灯是红色的,意味着有人正在使用厕所,那么他就需
        要等待。如果指示灯是绿色的,意味着厕所是空闲的,他就可以进去并将指示灯设置为
        红色,这样其他人就必须等待。
            在代码中,我们创建了一个Mutex对象来代表这个厕所。当一个线程想要访问共享资
        源时,它需要调用Mutex对象的WaitOne()方法来查看指示灯状态。如果指示灯是红色的
        (即Mutex被锁定),线程就会被阻塞等待。当指示灯是绿色的时候,线程可以继续执行,
        并调用Mutex对象的ReleaseMutex()方法将指示灯设置为红色,这样其他线程就必须等待。
        使用Mutex类可以确保共享资源在同一时间只能由一个线程访问,从而避免了数据竞争和
        冲突。它是一种有力的工具,用于实现多线程代码的同步和互斥操作。
    
    
    2、Mutex类
    
        Mutex类提供了对互斥锁进行操作和管理。常用的属性与方法:

        Name属性:获取或设置Mutex对象的名称。

        Mutex myMutex = new Mutex(false, "MyMutex");// 创建一个具有指定名称的Mutex对象string mutexName = myMutex.Name;// 获取Mutex对象的名称


        
        SafeWaitHandle属性:获取一个安全句柄,用于操作底层操作系统的同步原语。

        Mutex myMutex = new Mutex();// 创建一个Mutex对象SafeWaitHandle safeHandle = myMutex.SafeWaitHandle;// 获取SafeWaitHandle属性


        
        Mutex() 构造函数:创建一个Mutex对象。

        Mutex myMutex = new Mutex();// 创建一个Mutex对象,默认为非继承的互斥对象Mutex myInheritedMutex = new Mutex(true, "MyMutex", out bool createdNew);// 创建一个继承的互斥对象


        
        WaitOne() 方法:阻塞当前线程,直到Mutex对象变为可用。

        Mutex myMutex = new Mutex();// 创建一个Mutex对象myMutex.WaitOne();// 等待Mutex对象变为可用...// 执行需要互斥访问的代码myMutex.ReleaseMutex();// 释放Mutex对象


        
        WaitOne(TimeSpan timeout) 方法:阻塞当前线程,直到Mutex对象变为可用,或者等待超时。

        Mutex myMutex = new Mutex();// 创建一个Mutex对象TimeSpan timeout = TimeSpan.FromSeconds(2);// 设置等待超时时间为2秒bool mutexAcquired = myMutex.WaitOne(timeout);// 等待Mutex对象变为可用,或者等待超时if (mutexAcquired){...// 执行需要互斥访问的代码myMutex.ReleaseMutex();// 释放Mutex对象}else{...// 等待超时,执行其他操作}


        
        ReleaseMutex() 方法:释放Mutex对象,使其可供其他线程访问。

        Mutex myMutex = new Mutex();// 创建一个Mutex对象myMutex.WaitOne();// 等待Mutex对象变为可用...// 执行需要互斥访问的代码myMutex.ReleaseMutex();// 释放Mutex对象


        
        Close() 方法:释放并关闭Mutex对象。

        Mutex mutex = new Mutex();// 创建一个Mutex对象...// 执行一些操作mutex.Close();// 释放并关闭Mutex对象


        
        
        问:Mutex只能在一个线程中吗?
        答:不,Mutex在C#中并不限制只能在一个线程中使用。Mutex是一种系统级别的同步原
        语,可以用于跨线程、跨进程的互斥操作。
            在同一个进程中的多个线程可以使用同一个Mutex对象来实现互斥访问共享资源的目
        的。当一个线程获取到Mutex的锁时,其他线程将会被阻塞等待,直到锁被释放。这样可
        以确保同一个进程中的多个线程不会同时访问共享资源,避免出现冲突。
            此外,Mutex还可以用于跨进程的互斥操作。在不同的进程中,可以通过使用具有相
        同名称的Mutex对象来进行互斥操作。这样,不同进程中的线程就可以通过Mutex来同步
        对共享资源的访问。
            总结:Mutex既可以用于同一个线程内部的互斥操作,也可以用于不同线程、不同进
        程之间的互斥操作,以实现并发控制和资源保护。
        
        
        问:mutex中的Close与ReleaseMutex有什么区别?
        答:Close()方法用于释放Mutex对象占用的资源。当你不再需要使用Mutex对象时,可以
        调用Close()方法来显式地释放资源,以便后续的垃圾回收器可以回收相应资源。调用
        Close()方法后,你将无法再使用该Mutex对象。
            ReleaseMutex()方法用于释放Mutex对象的锁。当你在某个线程中调用WaitOne()方
        法获取了Mutex的锁之后,你可以在适当的时候调用ReleaseMutex()方法来释放该锁,以
        允许其他等待线程获得该锁并继续执行。每次调用WaitOne()方法成功后,需要对应调用
        ReleaseMutex()方法来释放锁。
            简言之:Close()方法用于释放对象;而ReleaseMutex()方法用于释放锁,对象仍在。
            
            
        问:WaitOne()与WaitOne(TimeSpan)的区别?
        答:两者都用于等待获取Mutex互斥锁。
            WaitOne():没有参数,它会一直等待直到获取到Mutex或超时。如果Mutex当前不可
        用,调用WaitOne()的线程将被阻塞,直到Mutex可用或者被中断。如果获取到Mutex,
        WaitOne()会返回true。如果等待过程中发生了异常或调用被中断,WaitOne()会返回
        false。
            WaitOne(TimeSpan):接受一个TimeSpan类型的参数,用于指定最大等待时间。如果
        Mutex在指定的时间内可用,该方法会获取到Mutex并返回true。如果Mutex在指定的时间
        内不可用,该方法会返回false,表示超时。如果等待过程中发生了异常或调用被中断,
        WaitOne(TimeSpan)会返回false。
            WaitOne()没有超时设置,会一直阻塞等待;而WaitOne(TimeSpan)允许设置最大等
        待时间,在超过该时间后会返回超时结果。WaitOne(0)立即返回结果。
            
        
        
    3、上面的Mutex在取互拆锁时,可能超时,怎么修正单例模式?
        

        [STAThread]private static void Main(){string mutexName = "MyapplicatonMutexApp1121";using (Mutex m = new Mutex(true, mutexName, out bool isFist)){if (isFist == false){MessageBox.Show("已经在运行了.");return;}bool acquiredLock = m.WaitOne(TimeSpan.FromSeconds(1));//等待1秒后尝试获取互斥锁if (!acquiredLock)//失败{MessageBox.Show("获取互斥锁超时。");return;//超时也认为在运行,所以需要人为再运行看提示。}Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}


        
        问:为什么会有延迟?
        答:获取互斥锁时,可能会出现延迟的情况有多种原因,导致已经释放,但获取失败。
            比如:线程调度、系统资源竞争、阻塞操作、垃圾回收等等,原因通常是在多线程
        环境下或与外部资源的交互中比较常见的。虽然我们不能完全消除延迟,但可以采取一
        些措施来最小化延迟的影响,例如使用合理的线程调度策略、避免过度竞争共享资源、
        进行异步操作以减少阻塞等。
        


三、应用举例


        一个常见的使用Mutex的经典例子是多个线程同时读写共享的全局变量。在这个例子中,
    我们使用Mutex来确保同一时间只有一个线程能够访问共享变量。

        class Program{static int sharedVariable = 0;static Mutex mutex = new Mutex();static void Main(string[] args){for (int i = 0; i < 5; i++)// 创建并启动多个线程{Thread t = new Thread(IncrementSharedVariable);t.Start();}Thread.Sleep(2000);// 等待所有线程执行完成Console.WriteLine("Shared Variable: " + sharedVariable);// 输出最终的共享变量的值Console.ReadLine();}static void IncrementSharedVariable(){mutex.WaitOne();// 等待获取Mutex的锁sharedVariable++;// 对共享变量进行操作mutex.ReleaseMutex();// 释放Mutex的锁}}


        在上面的例子中,有多个线程同时执行IncrementSharedVariable方法来对
    sharedVariable进行自增操作。由于使用了Mutex,每次只有一个线程能够获取到Mutex的锁,
    执行自增操作并释放锁。
        通过使用Mutex,我们确保了对sharedVariable的访问是互斥的,避免了多个线程同时
    对其进行写操作导致的数据竞争和不确定行为。最终,我们可以正确地得到共享变量的最终
    值。
        
        上面因为执行很快,所以直接用了等待2秒。下面用程序来判断线程都结束.

        internal class Program{private static int shareVariable = 0;private static Mutex mutex = new Mutex();private static void Main(string[] args){Thread[] threads = new Thread[5];for (int i = 0; i < 5; i++){threads[i] = new Thread(IncrementSharedVariable);threads[i].Start();}foreach (Thread thread in threads){thread.Join();}Console.WriteLine(shareVariable);Console.ReadKey();}private static void IncrementSharedVariable(){mutex.WaitOne();shareVariable++;mutex.ReleaseMutex();}}


    thread.Join()是什么意思?
        当主线程开始运行时,会迅速创建并启动5个线程。然后,在主线程中使用foreach循环
    遍历所有线程,并在每个线程上调用Join()方法,这会阻塞主线程,直到相应的线程执行完
    成。
        主线程会周期性地检查每个线程的状态,直到所有线程都执行完成。一旦所有线程执行
    完成,Join()方法会立即返回,主线程不再被阻塞,接下来的代码会继续执行。
        这种方式保证了主线程在输出最终的共享变量之前等待所有线程执行完成。它是一种有
    效的方式来确保所有线程完成后再继续程序的执行。
        简言之,主程序暂停,直到Join()里所有线程完成,主线程继续向下执行。
 

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

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

相关文章

【嵌入式Linux项目】基于Linux的全志H616开发板智能家居项目(语音控制、人脸识别、安卓APP和PC端QT客户端远程操控)有视频功能展示

目录 一、功能需求 二、开发环境 1、硬件&#xff1a; 2、软件&#xff1a; 3、引脚分配&#xff1a; 三、关键点 1、设计模式之工厂模式 2、wiringPi库下的相关硬件操作函数调用 3、语音模块的串口通信 4、线程 5、摄像头的实时监控和拍照功能 6、人脸识别 四、编…

网络互联究竟是需要什么协议相同,什么协议不同?

混淆概念的几个说法&#xff1a; 说法1&#xff1a; “以太网交换机不可以实现采用不同网络层协议的互联” 原因&#xff1a;以太网交换机是数据链路层的设备&#xff0c;不懂网络层的知识 说法2&#xff1a; “网桥可互联不同的物理层、不同的MAC子层以及不同速率的以太网”…

Spring Boot实践四 --集中式缓存Redis

随着时间的积累&#xff0c;应用的使用用户不断增加&#xff0c;数据规模也越来越大&#xff0c;往往数据库查询操作会成为影响用户使用体验的瓶颈&#xff0c;此时使用缓存往往是解决这一问题非常好的手段之一。Spring 3开始提供了强大的基于注解的缓存支持&#xff0c;可以通…

ios私钥证书的创建方法

ios私钥证书是苹果公司为ios开发者打包app&#xff0c;推出的一种数字证书&#xff0c;只有同一个苹果开发者账号生成的ios私钥证书打的包&#xff0c;才能上架同一个开发者账号的app store。因此不要指望别人给你共享私钥证书和描述文件&#xff0c;因为别人的证书和描述文件打…

Nginx下载、安装与使用

Nginx下载 简介&#xff1a; Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务&#xff08;邮件服务&#xff09;。 官网下载地址&#xff1a; https://nginx.org/en/download.html 国内镜像地址&#xff1a; https://mirrors.huawe…

基于RASC的keil电子时钟制作(瑞萨RA)(6)----定时器驱动数码管

基于RASC的keil电子时钟制作6_定时器驱动数码管 概述硬件准备视频教程选择定时器定时器做计数器配置定时器回调函数timer_smg.ctimer_smg.h演示效果主程序 概述 要想让每个数码管显示不同的数字&#xff0c;但是数码管必须依次地被持续驱动&#xff0c;数码管之间的刷新速度应…

【图论】Prim算法

一.介绍 Prim算法是一种用于解决最小生成树问题的贪心算法。最小生成树问题是指在一个连通无向图中找到一个生成树&#xff0c;使得树中所有边的权重之和最小。 Prim算法的基本思想是从一个起始顶点开始&#xff0c;逐步扩展生成树&#xff0c;直到覆盖所有顶点。具体步骤如下…

SpringBoot面试题及答案整理

1、什么是 Spring Boot&#xff1f; 多年来&#xff0c;随着新功能的增加&#xff0c;spring 变得越来越复杂。访问spring官网页面&#xff0c;我们就会看到可以在我们的应用程序中使用的所有 Spring 项目的不同功能。如果必须启动一个新的 Spring 项目&#xff0c;我们必须添…

动脑学院Jetpack Compose学习笔记

最近b站学习了一下Compose相关内容&#xff0c;整理了相关笔记&#xff0c;仅供大家参考。 资源链接如下&#xff0c;象征性收取1个积分 https://download.csdn.net/download/juliantem/88125198

网络运维总复习结构

目录 前言 基础运维技能 3种常用的字符编码&#xff0c;编码间的切换 简述文件的操作流程&#xff0c;三个常用的文件操作方法 open函数中的mode参数表示什么&#xff0c;mode的四个取值 阐述配置文件的格式&#xff0c;解析配置文件主要用到哪个模块的哪个类&#xff0c…

企业级docker应用注意事项

现在很多企业使用容器化技术部署应用&#xff0c;绕不开的docker技术&#xff0c;在生产环境docker常用操作总结。参考&#xff1a;https://juejin.cn/post/7259275893796651069 1. 尽可能使用官方镜像 在docker hub 官方 使用后面带有 DOCKER OFFICIAL IMAGE 标签的镜像&…

SOC FPGA介绍及开发设计流程

目录 一、SoC FPGA简介 二、SoC FPGA开发流程 2.1 硬件开发 2.2 软件开发 一、SoC FPGA简介 SOC FPGA是在FPGA架构中集成了基于ARM的硬核处理器系统(HPS)&#xff0c;包括处理器、外设和存储器控制器。相较于传统的仅有ARM处理器或 FPGA 的嵌入式芯片&#xff0c;SOC FPGA既…

CSS Flex 笔记

1. Flexbox 术语 Flex 容器可以是<div> 等&#xff0c;对其设置属性&#xff1a;display: flex, justify-content 是沿主轴方向调整元素&#xff0c;align-items 是沿交叉轴对齐元素。 2. Cheatsheet 2.1 设置 Flex 容器&#xff0c;加粗的属性为默认值 2.1.1 align-it…

jenkinsfile指定jenkins流水线的构建号

背景 升级Jenkins过程中不小心导致流水线配置文件job目录丢失, 重新配置流水线后所有流水线构建号码都从1开始构建了, 然而我们的产品关联了jenkins构建号,重新从1 构建会导致各种问题. 解决方案 在Jenkinsfile文件中指定流水线的构建号为一个不存在的数字, 这样就不会冲突了…

1,复杂度和简单排序算法【p2-p3】

复杂度和简单排序算法 1&#xff0c;时间复杂度1.1选择排序1.2冒泡排序1.3异或运算1.3.1性质&#xff1a;1.3.2案例例1例2 1.4插入排序1.5二分法1.5.1在一个有序数组中&#xff0c;找某个数是否存在1.5.2在一个有序数组中&#xff0c;找>某个数最左侧的位置1.5.3局部最小值问…

Individual household electric power consumption个人家庭用电量数据挖掘与时序预测建模

今天接到一个任务就是需要基于给定的数据集来进行数据挖掘分析相关的计算&#xff0c;并完成对未来时段内数据的预测建模&#xff0c;话不多说直接看内容。 官方数据详情介绍在这里&#xff0c;如下所示&#xff1a; 数据集中一共包含9个不同的字段&#xff0c;详情如下&#…

手把手一起实现Visual Studio 2022本地工程提交(和克隆)Gitee

1、VS2022本地工程提交Gitee 登录Gitee&#xff0c;创建空仓库&#xff0c;如图&#xff1a; 新建仓库&#xff1a; 打开Visual Studio 2022创建的工程&#xff0c;点击创建Git存储库&#xff1a; 复制Gitee仓库URL&#xff1a; 将URL填入&#xff0c;点击创建并推送&#xff…

Windows 10 安装 PostgreSQL 12.x 报错 ‘psql‘ 不是内部或外部命令 由于找不到文件libintl-9.dll等问题

目录 序言一、问题总结问题 1 psql 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。问题 2 “由于找不到文件libintl-9.dll&#xff0c;无法继续执行代码&#xff0c;重新安装程序可能会解决此问题。“1、卸载2、安装3、安装 Stack Builder &#xff08;这个可…

LeetCode.189(轮转数组)

对于轮转数组这个题&#xff0c;文章一共提供三种思路&#xff0c;对于每种思路均提供其对应代码的时间、空间复杂度。 目录 1. 创建变量来保存最后一个数&#xff0c;并将其余数组向前挪动一位 &#xff1a; 1.1 原理解析&#xff1a; 1.2 代码实现&#xff1a; 2.创建一个…

NFT和数字藏品的安全方案解析

一、NFT和数字藏品 01 NFT是什么&#xff1f; NFT 是Non-Fungible Tokens 的缩写&#xff0c;意思是不可互换的代币&#xff0c;它是相对于可互换的代币而言的。不可互换的代币也称为非同质代币。什么是可互换的代币&#xff1f;比如BTC&#xff08;比特币&#xff09;、ETH&…