Mutex C#等待释放,但是不执行代码

时间:2015-01-08 08:39:21

标签: c# multithreading mutex

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

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

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

2 个答案:

答案 0 :(得分:2)

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。第二个(或任何后续)流程将只打开现有的名为MutexcreatedNew变量将设置为false

重要的是,第一个进程还将获取 Mutex作为创建的一部分。这可确保其他任何进程都无法获取之前的Mutex。然后,任何后续流程都可能会尝试获取Mutex,该流程可以等待其可用。

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

答案 1 :(得分:0)

以下是使用Mutex的完整示例,其中可以添加资金并从帐户中提取。

请注意以下几点:

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

请检查this nice post(虽然它是葡萄牙语),以便在Mutex,Monitor设计模式和锁定构造之间进行有趣的比较。

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;
        }
    }
}