互斥 C# 等待发布,但不执行代码
本文关键字:执行 代码 等待 互斥 | 更新日期: 2023-09-27 18:36:46
我有一个 C# 控制台应用程序。此应用由其他几个不同的应用调用,但其中的代码只能由第一个调用方执行一次。
第二个调用方需要等待第一个调用方完成,而不是不执行代码。
我正在尝试将互斥对象与 WaitOne 一起使用,所以等待发布很简单,但比第二个调用者应该跳过互斥体内的代码......
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 如何安排进程。不过,他们都会轮到他们了。
下面是一个使用互斥体的完整示例,可以在其中添加资金并从帐户中提取资金。
请注意几点:
- 在构造函数中显示了几个陡峭的陡峭点,这将避免使用互斥体时的安全问题。此外,它还显示了如何正确命名全局互斥体。
- 调用 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;
}
}
}