【问题标题】:Acquire and release Mutex over interop通过互操作获取和释放 Mutex
【发布时间】:2013-09-26 15:02:38
【问题描述】:

我们有一个可以运行多个实例的旧版 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);
}

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

【问题讨论】:

  • 我注意到的一件事是你从来没有在MutexReleaseLock() 上打电话给Dispose()
  • 一个公平的观点,虽然我只是把它包装在一个 using 声明中:using (var mutex = Mutex.OpenExisting(mutexId)) 无济于事。
  • 另外,如果我将 Mutex 实例化为 new Mutex(false, mutexId, out mutexExists);,我会得到一个 ApplicationException: Object synchronization method was called from an unsynchronized block of code.
  • 您肯定需要重新考虑这一点。在您 Dispose() 互斥变量或它被垃圾收集之前,互斥锁不会消失。您的 ReleaseLock() 使情况变得更糟,它向系统对象添加了 another 引用。

标签: c# concurrency mutex


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-14
      • 1970-01-01
      • 1970-01-01
      • 2012-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-31
      相关资源
      最近更新 更多