0

我们有一个可以运行多个实例的旧版 VB6 可执行文件。我们希望某些工作只允许一个并发实例。

似乎 OS Mutex 非常适合,因为这是一个遗留应用程序,所有新代码都必须用 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);
}

获得锁的第二次尝试发现锁已经到位。为什么这个锁不释放?

4

2 回答 2

1

构造函数上的out值不是您想要返回以指示是否获取互斥锁的值。它仅指示指定的互斥锁名称是否是新的。initiallyOwned将(第一个参数)指定为false然后return mutex.WaitOne();

您可能希望将 AcquireLock 设置为“尝试获取锁”并超时。查看这个 SO answer以获得完整的示例。

于 2013-09-26T15:46:16.053 回答
0

感谢 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);
    }
}

到目前为止我所有的测试用例都通过了,欢迎评论。

于 2013-09-27T12:28:02.067 回答