2017-04-04 46 views
-2

对于执行异步文件IO,我制作了一个类,让我根据字符串键进行锁定,以防止同时对同一文件进行多次写入,或者防止写入和读取同时完成。然而,我面临的问题是,通过每次发送一个不同的密钥来增加_lockDict的可能性。旧的未使用的锁没有被清理,但我不知道如何以线程安全的方式做到这一点。这可能会导致非常大的内存消耗。清理NamedReaderWriterLocker中未使用的锁

,让背部基于钥匙的锁的实例类:

public class AsyncNamedReaderWriterLocker 
{ 
    private readonly object _mutex = new object(); 
    private readonly Dictionary<string, AsyncReaderWriterLock> _lockDict = new Dictionary<string, AsyncReaderWriterLock>(); 

    public Task<IDisposable> EnterReaderLockAsync(string name) 
    { 
     var locker = GetLock(name); 
     return locker.EnterReaderLockAsync(); 
    } 

    public Task<IDisposable> EnterWriterLockAsync(string name) 
    { 
     var locker = GetLock(name); 
     return locker.EnterWriterLockAsync(); 
    } 

    private AsyncReaderWriterLock GetLock(string name) 
    { 
     lock (_mutex) 
     { 
      if (!_lockDict.TryGetValue(name, out AsyncReaderWriterLock locker)) 
      { 
       locker = new AsyncReaderWriterLock(); 
       _lockDict.Add(name, locker); 
      } 
      return locker; 
     } 
    } 

和锁本身(创意来自:https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-7-asyncreaderwriterlock/):

public class AsyncReaderWriterLock 
{ 
    private readonly Queue<TaskCompletionSource<IDisposable>> _writerQueue = new Queue<TaskCompletionSource<IDisposable>>(); 
    private readonly Queue<TaskCompletionSource<IDisposable>> _readerQueue = new Queue<TaskCompletionSource<IDisposable>>(); 
    private readonly WriterLocker _writerLocker; 
    private readonly ReaderLocker _readerLocker; 
    private readonly object _mutex = new object(); 
    private int _locksHeld; 

    public AsyncReaderWriterLock() 
    { 
     _writerLocker = new WriterLocker(this); 
     _readerLocker = new ReaderLocker(this); 
    } 

    public Task<IDisposable> EnterReaderLockAsync() 
    { 
     lock (_mutex) 
     { 
      if (_locksHeld >= 0 && _writerQueue.Count == 0) 
      { 
       _locksHeld++; 
       return Task.FromResult<IDisposable>(_readerLocker); 
      } 
      var tcs = new TaskCompletionSource<IDisposable>(); 
      _readerQueue.Enqueue(tcs); 
      return tcs.Task; 
     } 
    } 

    public Task<IDisposable> EnterWriterLockAsync() 
    { 
     lock (_mutex) 
     { 
      if (_locksHeld == 0) 
      { 
       _locksHeld = -1; 
       return Task.FromResult<IDisposable>(_writerLocker); 
      } 
      var tcs = new TaskCompletionSource<IDisposable>(); 
      _writerQueue.Enqueue(tcs); 
      return tcs.Task; 
     } 
    } 

    private void ReleaseLocks() 
    { 
     if (_locksHeld != 0) 
      return; 

     // Give priority to writers. 
     if (_writerQueue.Count != 0) 
     { 
      _locksHeld = -1; 
      var tcs = _writerQueue.Dequeue(); 
      tcs.TrySetResult(_writerLocker); 
      return; 
     } 

     // Then to readers. 
     while (_readerQueue.Count != 0) 
     { 
      var tcs = _readerQueue.Dequeue(); 
      tcs.TrySetResult(_readerLocker); 
      ++_locksHeld; 
     } 
    } 

    private void ReleaseReaderLock() 
    { 
     lock (_mutex) 
     { 
      _locksHeld--; 
      ReleaseLocks(); 
     } 
    } 

    private void ReleaseWriterLock() 
    { 
     lock (_mutex) 
     { 
      _locksHeld = 0; 
      ReleaseLocks(); 
     } 
    } 

    private class ReaderLocker : IDisposable 
    { 
     private readonly AsyncReaderWriterLock _asyncReaderWriterLock; 

     internal ReaderLocker(AsyncReaderWriterLock asyncReaderWriterLock) 
     { 
      _asyncReaderWriterLock = asyncReaderWriterLock; 
     } 

     public void Dispose() 
     { 
      _asyncReaderWriterLock.ReleaseReaderLock(); 
     } 
    } 

    private class WriterLocker : IDisposable 
    { 
     private readonly AsyncReaderWriterLock _asyncReaderWriterLock; 

     internal WriterLocker(AsyncReaderWriterLock asyncReaderWriterLock) 
     { 
      _asyncReaderWriterLock = asyncReaderWriterLock; 
     } 

     public void Dispose() 
     { 
      _asyncReaderWriterLock.ReleaseWriterLock(); 
     } 
    } 
} 

使用AsyncNamedReaderWriterLocker类的实例:

public class AsyncFileIO 
{ 
    private static readonly AsyncNamedReaderWriterLocker AsyncNamedReaderWriterLocker = new AsyncNamedReaderWriterLocker(); 

    public async Task CreateFile(string filename, byte[] data) 
    { 
     using (await AsyncNamedReaderWriterLocker.EnterWriterLockAsync(filename)) 
     { 
      var directory = Path.GetDirectoryName(filename); 
      if (!Directory.Exists(directory)) 
      { 
       Directory.CreateDirectory(directory); 
      } 
      using (var stream = File.Create(filename)) 
      { 
       await stream.WriteAsync(data, 0, data.Length); 
      } 
     } 
    } 

    public async Task<byte[]> ReadFile(string filename) 
    { 
     using (await AsyncNamedReaderWriterLocker.EnterReaderLockAsync(filename)) 
     { 
      if (!File.Exists(filename)) return null; 
      using (var stream = File.OpenRead(filename)) 
      { 
       var data = new byte[stream.Length]; 
       await stream.ReadAsync(data, 0, 0); 
       return data; 
      } 
     } 
    } 

    public async Task DeleteFile(string filename) 
    { 
     using (await AsyncNamedReaderWriterLocker.EnterWriterLockAsync(filename)) 
     { 
      var directoryName = Path.GetDirectoryName(filename); 
      if (!Directory.Exists(directoryName)) return; 
      File.Delete(filename); 
     } 
    } 
} 

注意iam最感兴趣的是这个l赚钱的目的,并充分认识到这对大多数应用程序来说是矫枉过正的。

+0

您需要添加一种方法将锁标记为未使用。这可能会在任何客户端发送文件名时完成。一旦决定不再需要文件名,它可以告诉你的'AsyncNamedReaderWriterLocker'转储锁。这就是说,同一个角色可以很容易地协调谁读写什么文件,这样就不需要并发文件访问。为了获得更具体的内容,准确地知道你要完成的是什么,以及为什么你需要并发的文件访问而不是异步访问? – JSteward

+0

随着异步的事情也会出现并发。您可以尝试同时读取和写入同一个文件,导致并发性。这就是为什么需要锁来防止同时写入和读取同一个文件的原因。此代码将用于服务器中以提供文件和一些其他逻辑,例如生成图像缩略图。我正在寻找的是一旦不再使用锁(在线程中),锁将被清除。 – Barsonax

+0

你将不得不跟踪有多少线程持有或等待着锁(你的锁似乎已经有这个信息),然后当一个锁被释放时,如果没有其他线程在等待,就从你的字典。当然,这将需要在发布步骤进行额外的同步,以确保在您释放它的过程中没有线程检索到字典中的锁。我怀疑这比仅仅对所有资源使用一个锁定更好(尤其是考虑到所有I/O操作瓶颈的可能性)。 –

回答

0

起初,我在尝试同步异步命名锁以便移除未使用的锁时遇到了死锁。这是因为并非所有的锁都是以相同的顺序发布的。解决这个问题需要一个专门的AsyncReaderWriter锁,仅用于这个异步命名锁。它现在看起来像这样:

public class NamedAsyncReaderWriterLockController<TKey> 
{ 
    private readonly object _mutex = new object(); 
    private readonly Dictionary<TKey, NamedAsyncReaderWriterLock> _lockDict = new Dictionary<TKey, NamedAsyncReaderWriterLock>(); 


    public Task<IDisposable> EnterReaderLockAsync(TKey name) 
    { 
     lock (_mutex) 
     { 
      var locker = GetLock(name); 
      return locker.EnterReaderLockAsync(); 
     } 
    } 

    public Task<IDisposable> EnterWriterLockAsync(TKey name) 
    { 
     lock (_mutex) 
     { 
      var locker = GetLock(name); 
      return locker.EnterWriterLockAsync(); 
     } 
    } 

    private NamedAsyncReaderWriterLock GetLock(TKey name) 
    { 
     NamedAsyncReaderWriterLock locker; 
     if (!_lockDict.TryGetValue(name, out locker)) 
     { 
      locker = new NamedAsyncReaderWriterLock(this, name, _mutex); 
      _lockDict.Add(name, locker); 
     } 
     return locker; 
    } 

    private void RemoveLock(TKey name) 
    { 
     _lockDict.Remove(name); 
    } 

    private class NamedAsyncReaderWriterLock 
    { 
     private readonly TKey _name; 
     private readonly NamedAsyncReaderWriterLockController<TKey> _namedAsyncReaderWriterLockController; 
     private readonly Queue<TaskCompletionSource<IDisposable>> _writerQueue = new Queue<TaskCompletionSource<IDisposable>>(); 
     private readonly Queue<TaskCompletionSource<IDisposable>> _readerQueue = new Queue<TaskCompletionSource<IDisposable>>(); 
     private readonly NamedWriterLock _namedWriterLock; 
     private readonly NamedReaderLock _namedReaderLock; 
     private readonly object _mutex = new object(); 
     private readonly object _releaseMutex; 
     private int _locksHeld; 

     public NamedAsyncReaderWriterLock(NamedAsyncReaderWriterLockController<TKey> namedAsyncReaderWriterLockController, TKey name, object releaseMutex) 
     { 
      _namedWriterLock = new NamedWriterLock(this); 
      _namedReaderLock = new NamedReaderLock(this); 
      _releaseMutex = releaseMutex; 
      _name = name; 
      _namedAsyncReaderWriterLockController = namedAsyncReaderWriterLockController; 
     } 

     public Task<IDisposable> EnterReaderLockAsync() 
     { 
      lock (_mutex) 
      { 
       if (_locksHeld >= 0 && _writerQueue.Count == 0) 
       { 
        _locksHeld++; 
        return Task.FromResult<IDisposable>(_namedReaderLock); 
       } 
       var tcs = new TaskCompletionSource<IDisposable>(); 
       _readerQueue.Enqueue(tcs); 
       return tcs.Task; 
      } 
     } 

     public Task<IDisposable> EnterWriterLockAsync() 
     { 
      lock (_mutex) 
      { 
       if (_locksHeld == 0) 
       { 
        _locksHeld = -1; 
        return Task.FromResult<IDisposable>(_namedWriterLock); 
       } 
       var tcs = new TaskCompletionSource<IDisposable>(); 
       _writerQueue.Enqueue(tcs); 
       return tcs.Task; 
      } 
     } 

     private void ReleaseLocks() 
     { 
      if (_locksHeld != 0) 
       return; 

      // Give priority to writers. 
      if (_writerQueue.Count != 0) 
      { 
       _locksHeld = -1; 
       var tcs = _writerQueue.Dequeue(); 
       tcs.TrySetResult(_namedWriterLock); 
       return; 
      } 

      // Then to readers. 
      while (_readerQueue.Count != 0) 
      { 
       var tcs = _readerQueue.Dequeue(); 
       tcs.TrySetResult(_namedReaderLock); 
       ++_locksHeld; 
      } 

      if (_locksHeld == 0) _namedAsyncReaderWriterLockController.RemoveLock(_name); 
     } 

     private void ReleaseReaderLock() 
     { 
      lock (_releaseMutex) 
      { 
       lock (_mutex) 
       { 
        _locksHeld--; 
        ReleaseLocks(); 
       } 
      } 
     } 

     private void ReleaseWriterLock() 
     { 
      lock (_releaseMutex) 
      { 
       lock (_mutex) 
       { 
        _locksHeld = 0; 
        ReleaseLocks(); 
       } 
      } 
     } 

     private class NamedReaderLock : IDisposable 
     { 
      private readonly NamedAsyncReaderWriterLock _namedAsyncReaderWriterLock; 

      internal NamedReaderLock(NamedAsyncReaderWriterLock namedAsyncReaderWriterLock) 
      { 
       _namedAsyncReaderWriterLock = namedAsyncReaderWriterLock; 
      } 

      public void Dispose() 
      { 
       _namedAsyncReaderWriterLock.ReleaseReaderLock(); 
      } 
     } 

     private class NamedWriterLock : IDisposable 
     { 
      private readonly NamedAsyncReaderWriterLock _namedAsyncReaderWriterLock; 

      internal NamedWriterLock(NamedAsyncReaderWriterLock namedAsyncReaderWriterLock) 
      { 
       _namedAsyncReaderWriterLock = namedAsyncReaderWriterLock; 
      } 

      public void Dispose() 
      { 
       _namedAsyncReaderWriterLock.ReleaseWriterLock(); 
      } 
     } 
    }