通过互操作获取和释放互斥体
本文关键字:释放 互操作 获取 | 更新日期: 2023-09-27 18:32:51
我们有一个可以运行多个实例的旧版VB6可执行文件。我们希望某些作业只允许一个并发实例。
操作系统互斥体似乎是一个完美的选择,因为这是一个遗留的应用程序,所有新代码都必须用 C# 编写并通过互操作访问。
我创建了一个类,它将获得:
public bool AcquireLock(string JobId)
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
appGuid = appGuid + JobId;
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global''{{{0}}}", appGuid);
bool mutexExists = false;
var mutex = new Mutex(true, mutexId, out mutexExists);
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
return mutexExists;
}
和释放锁:
public bool ReleaseLock(string JobId)
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
appGuid = appGuid + JobId;
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global''{{{0}}}", appGuid);
var mutex = Mutex.OpenExisting(mutexId);
mutex.ReleaseMutex();
return true;
}
在我尝试释放锁之前,一切似乎都运行良好:
[TestMethod()]
public void ReleaseLockTest()
{
var target = new MutexConcurrencyHelper();
var jobId = RandomUtils.RandomString(8, true);
var expected = true;
bool actual;
actual = target.AcquireLock(jobId);
Assert.AreEqual(expected, actual);
target.ReleaseLock(jobId);
var expected1 = true;
bool actual1;
actual1 = target.AcquireLock(jobId);
Assert.AreEqual(expected1, actual1);
}
第二次尝试获得锁时发现锁已经就位。为什么这个锁不松开?
构造
函数上的out
值不是您要返回的值,以指示是否获取了互斥锁。 它仅指示指定的互斥锁名称是否为新名称。 将initiallyOwned
(第一个参数)指定为 false
,然后return mutex.WaitOne();
您可能希望使 AcquireLock 作为超时的"尝试获取锁定"工作。 查看此SO答案以获取完整示例。
感谢 Hans,我创建了一个更简单的解决方案,这也使我们能够限制正在运行的实例数量,这也是可取的:
编辑:添加了GenerateMutexId以确保完整性。
class SemaphoreConcurrencyHelper: IConcurrencyHelper
{
private Semaphore _semaphore;
public bool AcquireLock(string LockId, int Instances)
{
try
{
_semaphore = Semaphore.OpenExisting(GenerateMutexId(LockId)); //Acquire existing Semaphore (if exists)
}
catch (WaitHandleCannotBeOpenedException) // Create new Semaphore if not exists
{
_semaphore = new Semaphore(Instances, Instances, GenerateMutexId(LockId));
}
return _semaphore.WaitOne(TimeSpan.FromSeconds(10), false); // Block thread until Semaphore has slot available within specified Timespan
}
public bool ReleaseLock()
{
try
{
_semaphore.Release(1); // Increment the count on the Sempahore by 1
}
catch (Exception e)
{
return false; //TODO: Add an error log
}
_semaphore = null;
return true;
}
private string GenerateMutexId(string LockId)
{
// Get application GUID as defined in AssemblyInfo.cs
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
appGuid = appGuid + LockId;
// Unique id for global mutex - Global prefix means it is available system wide
return string.Format("Global''{{{0}}}", appGuid);
}
}
到目前为止,我所有的测试用例都通过了,欢迎评论。