Form1单例模式与互斥锁

news/2024/5/10 14:48:14

一、使用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.mrgr.cn/p/43102034

相关文章

微软亚研院提出模型基础架构RetNet或将成为Transformer有力继承者

作为全新的神经网络架构&#xff0c;RetNet 同时实现了良好的扩展结果、并行训练、低成本部署和高效推理。这些特性将使 RetNet 有可能成为继 Transformer 之后大语言模型基础网络架构的有力继承者。实验数据也显示&#xff0c;在语言建模任务上&#xff1a; RetNet 可以达到与…

【机器学习】Cost Function for Logistic Regression

Cost Function for Logistic Regression 1. 平方差能否用于逻辑回归&#xff1f;2. 逻辑损失函数loss3. 损失函数cost附录 导入所需的库 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_logistic_loss import plt_logistic_cost, plt_two_…

在windows上安装minio

1、下载windows版的minio&#xff1a; https://dl.min.io/server/minio/release/windows-amd64/minio.exe 2、在指定位置创建一个名为minio文件夹&#xff0c;然后再把下载好的文件丢进去&#xff1a; 3、右键打开命令行窗口&#xff0c;然后执行如下命令&#xff1a;(在minio.…

IDEA live templates

surround 在SQL的xml里 可以修改变量 官方文档 CDATA not null <if test"$SELECTION$ ! null and $SELECTION$ ! "> and $VAR1$ #{$SELECTION$} </if>not null like mysql <if test"$SELECTION$ ! null and $SELECTION$ ! "> and…

HDFS异构存储详解

异构存储 HDFS异构存储类型什么是异构存储异构存储类型如何让HDFS知道集群中的数据存储目录是那种类型存储介质 块存储选择策略选择策略说明选择策略的命令 案例&#xff1a;冷热温数据异构存储对应步骤 HDFS内存存储策略支持-- LAZY PERSIST介绍执行使用 HDFS异构存储类型 冷…

关于uniapp中的日历组件uni-calendar中的小红点

关于uniapp中的日历组件uni-calendar中的小红点 如果你使用过uni-calendar组件&#xff0c;可能你觉得这个小红点有点碍眼&#xff0c;但是官方给定的日历组件uni-calendar中如果你想要在某一天上添加一些信息例如:价格&#xff0c;签到&#xff0c;打卡之类&#xff0c;只要标…

Excel快捷键F1-F9详解:掌握实用快捷操作,提升工作效率

Excel是广泛应用于办公场景的优质电子表格软件&#xff0c;然而&#xff0c;许多人只是使用鼠标点击菜单和工具栏来完成操作&#xff0c;而忽略了快捷键的威力。在本文中&#xff0c;我们将详解Excel中的F1-F9快捷键&#xff0c;帮助您掌握实用的快捷操作&#xff0c;提升工作效…

NetSuite ERP顾问的进阶之路

目录 1.修养篇 1.1“道”是什么&#xff1f;“器”是什么&#xff1f; 1.2 读书这件事儿 1.3 十年计划的力量 1.3.1 一日三省 1.3.2 顾问损益表 1.3.3 阶段课题 2.行为篇 2.1协作 2.2交流 2.3文档管理 2.4时间管理 3.成长篇 3.1概念能力 3.1.1顾问的知识结构 …

模拟一个一维排斥场

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A有6个1&#xff0c;B有4个1&#xff0c;比较迭代次数的顺序。 其中有12组数据 差值结构 迭代次数 全0行 位置 构造平均列 平均列 列排斥能 …

3d软件动物生活习性仿真互动教学有哪些优势

软体动物是一类广泛存在于海洋和淡水环境中的生物&#xff0c;其独特的形态和生活习性给学生带来了新奇和有趣的学习主题&#xff0c;为了方便相关专业学科日常授课教学&#xff0c;web3d开发公司深圳华锐视点基于真实的软体动物&#xff0c;制作软体动物3D虚拟展示系统&#x…

Linux新手小程序——进度条

前言 目录 前言 需要先了解 1.\r和\n 2.缓冲区 一.理解字符的含义&#xff1a; 学习c语言时&#xff0c;我们可以粗略把字符分为可显字符和控制字符. 在按回车换到下一行开始的操作时&#xff0c;实际上是进行了两个操作&#xff1a;1.让光标跳到下一行&#xff08;只…

明晚直播:可重构计算芯片的AI创新应用分享!

大模型技术的不断升级及应用落地&#xff0c;正在推动人工智能技术发展进入新的阶段&#xff0c;而智能化快速增长和发展的市场对芯片提出了更高的要求&#xff1a;高算力、高性能、灵活性、安全性。可重构计算区别于传统CPU、GPU&#xff0c;以指令驱动的串行执行方式&#xf…

pytorch学习-线性神经网络——softmax回归+损失函数+图片分类数据集

1.softmax回归 Softmax回归&#xff08;Softmax Regression&#xff09;是一种常见的多分类模型&#xff0c;可以用于将输入变量映射到多个类别的概率分布中。softmax回归是机器学习中非常重要并且经典的模型&#xff0c;虽然叫回归&#xff0c;实际上是一个分类问题 1.1分类与…

站外引流效果差?一文带你搞懂解海外主流社交媒体算法!

在流量成本越来越高的当下&#xff0c;无论是平台卖家还是独立站卖家都在努力拓展流量渠道。站外引流是推动业务增长的关键策略&#xff0c;很多卖家会把重点放在内容营销上&#xff0c;但其实除了做好内容之前&#xff0c;了解社交媒体的算法才能让营销效果最大化。 01.Faceb…

大学生活题解

样例输入&#xff1a; 3 .xA ... Bx.样例输出&#xff1a; 6思路分析&#xff1a; 这道题只需要在正常的广搜模板上多维护一个— —方向&#xff0c;如果当前改变方向&#xff0c;就坐标不变&#xff0c;方向变&#xff0c;步数加一&#xff1b;否则坐标变&#xff0c;方向不…

uniapp使用getStorage对属性赋值无效

1正常set(get)storage都是可以正常使用的 2.但对属性进行赋值的时候&#xff0c;却发现this.name并没有发生变化 3. 在里面打印this发现&#xff0c;在set*getStorage中并不能拿到this. 4.优化代码 这样就可以给this.name成功赋值

EPICS通道访问介绍以及练习

提纲 1&#xff09; 通道访问概念 2&#xff09;通道访问API 3&#xff09; 简单的CA客户端 4&#xff09;使用回调的简单CA客户端 EPICS概要 搜索和连接过程 搜索请求 1&#xff09;搜索请求由一系列UDP包组成 只发送给EPICS_CA_ADDR_LIST从短时间间隔开始&#xff0c;每…

Linux标准库API

目录 1.字符串函数 2.数据转换函数 3.格式化输入输出函数 4.权限控制函数 5.IO函数 6.进程控制函数 7.文件和目录函数 1.字符串函数 2.数据转换函数 3.格式化输入输出函数 #include<stdarg.h>void test(const char * format , ...){va_list ap;va_start(ap,format…

IntelliJ IDEA流行的构建工具——Gradle

IntelliJ IDEA&#xff0c;是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 如…

【Docker】Docker应用部署之Docekr容器安装Nginx

目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search nginx 二、拉取镜像 docker pull nginx # 不加冒号版本号 默认拉取最新版 三、创建容器 首先我们需要在宿主机创建数据卷目录 mkdir nginx # 创建目录 cd nginx # 进入目录 mkd…