互斥 C# 等待发布,但不执行代码

本文关键字:执行 代码 等待 互斥 | 更新日期: 2023-09-27 18:36:46

我有一个 C# 控制台应用程序。此应用由其他几个不同的应用调用,但其中的代码只能由第一个调用方执行一次。

第二个调用方

需要等待第一个调用方完成,而不是不执行代码。

我正在尝试将互斥对象与 WaitOne 一起使用,所以等待发布很简单,但比第二个调用者应该跳过互斥体内的代码......

互斥 C# 等待发布,但不执行代码

Mutex 可以工作,但必须正确使用:

static void Main(string[] args)
{
    bool createdNew;
    Mutex mutex = new Mutex(true, "TestSO27835942", out createdNew);
    if (createdNew)
    {
        Console.WriteLine("First process!");
    }
    else
    {
        Console.WriteLine("Second process...waiting for first process");
        mutex.WaitOne();
        Console.WriteLine("First process has completed");
    }
    Console.WriteLine("Press return to exit...");
    Console.ReadLine();
    mutex.ReleaseMutex();
}

此代码创建或打开现有的命名Mutex。执行代码的第一个进程将创建MutexcreatedNew变量将设置为 true 。第二个(或任何后续)进程将简单地打开现有的命名Mutex,并且createdNew变量将设置为 false

重要的是,第一个过程也将获得Mutex作为创作的一部分。这可确保没有其他进程可以获取它之前的Mutex。然后任何后续进程都可能尝试获取Mutex,这将让该进程等到它可用。

最后注意,在第一个过程之后,没有特定的排序。第一个过程,即创建Mutex的过程,将始终首先获取它。但在那之后,它只取决于 Windows 如何安排进程。不过,他们都会轮到他们了。

下面是一个使用互斥体的完整示例,可以在其中添加资金并从帐户中提取资金。

请注意几点:

  • 在构造函数中显示了几个陡峭的陡峭点,这将避免使用互斥体时的安全问题。此外,它还显示了如何正确命名全局互斥体。
  • 调用 AFter mutex.WaitOne(MUTEX_WAIT_TIMEOUT, false);,而不是验证是否获取了锁。如果是,则代码正常进行,否则将引发超时异常。这是一个很好的做法,因此您可以检测互斥体中何时发生超时。
  • 到目前为止,这是最重要的观察结果:请注意,mutex.ReleaseMutex();代码是在finally子句中调用的。通过在 finally 子句中释放互斥锁,您保证,如果发生意外异常,互斥锁无论如何都会被释放。如果在 finally 子句中不这样做,并且抛出意外的异常,则在释放互斥体之前,代码可能会无限期锁定,并且您的程序可能会简单地冻结。

请检查这篇不错的帖子(虽然是葡萄牙语),了解互斥体、监视器设计模式和锁结构之间的有趣比较。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security.AccessControl;
using System.Security.Principal;
namespace MutexArticle
{
    class BankAccountMutex
    {
        private double bankMoney = 0d;
        Mutex mutex = null;
        private const int MUTEX_WAIT_TIMEOUT = 5000;
        // Note: configuration based on stackoverflow answer: http://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c
        public BankAccountMutex(double money)
        {
            // get application GUID as defined in AssemblyInfo.cs
            string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
            // unique id for global mutex - Global prefix means it is global to the machine
            string mutexId = string.Format("Global''{{{0}}}", appGuid);
            // Need a place to store a return value in Mutex() constructor call
            bool createdNew;
            // set up security for multi-user usage
            // work also on localized systems (don't use just "Everyone") 
            var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
            var securitySettings = new MutexSecurity();
            securitySettings.AddAccessRule(allowEveryoneRule);
            mutex = new Mutex(false, mutexId, out createdNew, securitySettings);
            LogConsole("Setting initial amount of money: " + money);
            if (money < 0)
            {
                LogConsole("The entered money quantity cannot be negative. Money: " + money);
                throw new ArgumentException(GetMessageWithTreadId("The entered money quantity cannot be negative. Money: " + money));
            }
            this.bankMoney = money;
        }
        public void AddMoney(double money = 0) 
        {
            bool hasHandle = mutex.WaitOne(MUTEX_WAIT_TIMEOUT, false);
            if (!hasHandle)
            {
                throw new TimeoutException(GetMessageWithTreadId("Method void AddMoney(double): Timeout due to look waiting."));
            }
            try
            {
                LogConsole("Money to be added: " + money);
                if (money < 0)
                {
                    LogConsole("The entered money quantity cannot be negative. Money: " + money);
                    throw new ArgumentException(GetMessageWithTreadId("The entered money quantity cannot be negative. Money: " + money));
                }
                this.bankMoney = this.bankMoney + money;
                if (this.bankMoney < 0)
                {
                    LogConsole("The money quantity cannot be negative. Money: " + money);
                    throw new ArgumentException(GetMessageWithTreadId("The money quantity cannot be negative. Money: " + money));
                }
                LogConsole("Total amount of money: " + this.bankMoney);
            }
            finally
            {
                if (hasHandle)
                {
                    mutex.ReleaseMutex();
                }
            }
        }
        public void WithdrawMoney(double money = 0)
        {
            bool hasHandle = mutex.WaitOne(MUTEX_WAIT_TIMEOUT, false);
            if (!hasHandle)
            {
                throw new TimeoutException(GetMessageWithTreadId("Method void WithdrawMoney(double): Timeout due to look waiting."));
            }
            try
            {
                if (money < 0)
                {
                    LogConsole("The entered money quantity cannot be negative. Money: " + money);
                    throw new ArgumentException(GetMessageWithTreadId("The entered money quantity cannot be negative. Money: " + money));
                }
                LogConsole("Money to be withdrawed: " + money);
                this.bankMoney = this.bankMoney - money;
                if (this.bankMoney < 0)
                {
                    LogConsole("The money quantity cannot be negative. Money: " + money);
                    throw new ArgumentException(GetMessageWithTreadId("The money quantity cannot be negative. Money: " + money));
                }
                LogConsole("Total amount of money: " + this.bankMoney);
            }
            finally
            {
                if (hasHandle)
                {
                    mutex.ReleaseMutex();
                }
            }
        }
        public double GetBankStatement()
        {
            LogConsole("Bank Statement: Total amount of money: " + this.bankMoney);
            return bankMoney;
        }
        private String getCurrenThreadId()
        {
            return Thread.CurrentThread.ManagedThreadId.ToString();
        }
        private void LogConsole(String message)
        {
            Console.WriteLine(GetMessageWithTreadId(message));
        }
        private String GetMessageWithTreadId(String message)
        {
            return "Thread ID: " + getCurrenThreadId() + ": " + message;
        }
    }
}