在.NET中对两个对象进行单独锁定

时间:2017-03-01 18:24:13

标签: .net multithreading locking mutex

我有一个.NET类,有三个方法:Get1(),Get2()和Disconnect()。 Get1和Get2可以在两个线程中同时调用,但我需要在Disconnect和其他两个方法之间保留互斥。换句话说,我不希望它通过“get”中途断开连接。

我可以使用两个锁来执行此操作,如下所示:

private object lock1 = new object();
private object lock2 = new object();

public void Get1()
{
  lock(lock1)
  {
    // work
  }
}

public void Get2()
{
  lock(lock2)
  {
    // work
  }
}

public void Disconnect()
{
  lock(lock1)
  {
    lock(lock2)
    {
      // work
    }
  }
}

这是实现上述结果的最佳做法吗?我应该关注死锁吗?如果有两个以上可并行化的方法 - 拥有 n 锁定似乎不是一个好的解决方案。

1 个答案:

答案 0 :(得分:0)

修改
我正在考虑更多关于这个问题,我知道它符合一个模式,但不记得它是什么,然后我浏览了System.Threading类并找到了它 - 这符合Reader / Writer模式,并且有一个锁定 - ReaderWriterLockSlim

这是一个使用此锁的示例类,并且具有可以在get和disconnect调用中传递的方法(或者您可以将此锁添加到您的类中并按照示例中的模式进行操作)

public class GetAndDisconnectLocker : IDisposable
{
    private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

    public void ExecuteGet(Action action)
    {
        _lock.EnterReadLock();
        try
        {
            action();
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public T ExecuteGet<T>(Func<T> function)
    {
        _lock.EnterReadLock();
        try
        {
            return function();
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public void ExecuteDisconnect(Action action)
    {
        _lock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (disposing && _lock != null)
        {
            _lock.Dispose();
            _lock = null;
        }
    }
}

原始答案
在我的头顶,我可能会用ManualResetEvents处理这个问题。您可以为每种方法创建一个事件,并使用Disconnect方法中的WaitAll确保所有Get方法都已完成,然后再继续Disconnect。如果您希望Get方法运行得相当短,您可能需要考虑ManualResetEventSlim哪个应该更适合短期运行的进程。它使用旋转而不是事件句柄。

ManualResetEvent get1 = new ManualResetEvent(false);
ManualResetEvent get2 = new ManualResetEvent(false);
ManualResetEvent disc = new ManualResetEvent(false);

public void Get1()
{
    disc.WaitOne();
    get1.Reset();
    try
    {
        // Do stuff
    }
    finally
    {
        get1.Set();
    }
}

public void Get2()
{
    disc.WaitOne();
    get2.Reset();
    try
    {
        // Do stuff
    }
    finally
    {
        get2.Set();
    }
}

public void Disconnect()
{
    disc.Reset();
    try
    {
        WaitHandle.WaitAll(new [] { get1, get2 });
        // Do Stuff
    }
    finally
    {
        disc.Set();
    }
}