我有一个C#控制台应用程序。这个应用程序由几个不同的其他应用程序调用,但内部代码只能由第一个调用者执行一次。
第二个调用者需要等待第一个调用者完成,而不是执行代码。
我正在尝试将Mutex对象与WaitOne一起使用,所以等待发布很简单,但是第二个调用者应该跳过Mutex中的代码......
答案 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
的代码。执行代码的第一个过程将创建Mutex
,createdNew
变量将设置为true
。第二个(或任何后续)流程将只打开现有的名为Mutex
,createdNew
变量将设置为false
。
重要的是,第一个进程还将获取 Mutex
作为创建的一部分。这可确保其他任何进程都无法获取之前的Mutex
。然后,任何后续流程都可能会尝试获取Mutex
,该流程可以等待其可用。
最后请注意,在第一个过程之后,没有特定的排序。第一个进程,即创建Mutex
的进程,将始终首先获取它。但在那之后,它只取决于Windows如何安排流程。他们都会轮到他们了。
答案 1 :(得分:0)
以下是使用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;
}
}
}