C# 清理NameDrawerWriterLocker中未使用的锁
对于异步文件IO,我创建了一个类,它允许我基于字符串键进行锁定,以防止对同一文件同时进行多次写入,或防止同时进行写入和读取。然而,我面临的问题是,通过每次发送不同的密钥,可能会增加锁的大小。旧的未使用的锁没有清理,但我不知道如何以线程安全的方式进行清理。这可能会导致非常大的内存消耗 基于密钥返回锁实例的类:C# 清理NameDrawerWriterLocker中未使用的锁,c#,multithreading,asynchronous,thread-safety,locking,C#,Multithreading,Asynchronous,Thread Safety,Locking,对于异步文件IO,我创建了一个类,它允许我基于字符串键进行锁定,以防止对同一文件同时进行多次写入,或防止同时进行写入和读取。然而,我面临的问题是,通过每次发送不同的密钥,可能会增加锁的大小。旧的未使用的锁没有清理,但我不知道如何以线程安全的方式进行清理。这可能会导致非常大的内存消耗 基于密钥返回锁实例的类: public class AsyncNamedReaderWriterLocker { private readonly object _mutex = new object
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;
}
}
公共类AsyncNameDrawerWriterLocker
{
私有只读对象_mutex=新对象();
专用只读词典_lockDict=新词典();
公共任务EnterReaderLockAsync(字符串名称)
{
var locker=GetLock(名称);
return locker.entreReaderLockAsync();
}
公共任务EnterWriterLockAsync(字符串名称)
{
var locker=GetLock(名称);
return locker.EnterWriterLockAsync();
}
专用AsyncReaderWriterLock GetLock(字符串名称)
{
锁(互斥)
{
if(!\u lockDict.TryGetValue(名称,out AsyncReaderWriterLocker))
{
locker=新的AsyncReaderWriterLock();
_添加(姓名、储物柜);
}
返回储物柜;
}
}
锁本身(创意来源:):
公共类AsyncReaderWriterLock
{
私有只读队列_writerQueue=新队列();
私有只读队列_readerQueue=new Queue();
专用只读WriterLocker\u WriterLocker;
专用只读读卡器\u读卡器;
私有只读对象_mutex=新对象();
私人国际洛克谢尔德酒店;
公共AsyncReaderWriterLock()
{
_writerLocker=新的writerLocker(此);
_readerLocker=新的readerLocker(此);
}
公共任务EnterReaderLockAsync()
{
锁(互斥)
{
如果(_locksHeld>=0&&u writerQueue.Count==0)
{
_locksHeld++;
返回Task.FromResult(_readerLocker);
}
var tcs=new TaskCompletionSource();
_readerQueue.Enqueue(tcs);
返回tcs.Task;
}
}
公共任务EnterWriterLockAsync()
{
锁(互斥)
{
如果(_locksHeld==0)
{
_locksHeld=-1;
返回Task.FromResult(_writerLocker);
}
var tcs=new TaskCompletionSource();
_writerQueue.Enqueue(tcs);
返回tcs.Task;
}
}
私有无效释放锁()
{
如果(_locksHeld!=0)
回来
//优先考虑作家。
如果(_writerQueue.Count!=0)
{
_locksHeld=-1;
var tcs=_writerQueue.Dequeue();
tcs.TrySetResult(_writerLocker);
回来
}
//然后给读者。
而(_readerQueue.Count!=0)
{
var tcs=_readerQueue.Dequeue();
tcs.TrySetResult(_readerLocker);
++_洛克谢尔德;
}
}
私有void ReleaseReaderLock()
{
锁(互斥)
{
_洛克谢尔德;
释放锁();
}
}
私有void ReleaseWriterLock()
{
锁(互斥)
{
_locksHeld=0;
释放锁();
}
}
私有类ReaderLocker:IDisposable
{
私有只读AsyncReaderWriterLock\u AsyncReaderWriterLock;
内部ReaderLocker(AsyncReaderWriterLock AsyncReaderWriterLock)
{
_asyncReaderWriterLock=asyncReaderWriterLock;
}
公共空间处置()
{
_asyncReaderWriterLock.ReleaseReaderLock();
}
}
私有类WriterLocker:IDisposable
{
私有只读AsyncReaderWriterLock\u AsyncReaderWriterLock;
内部WriterLocker(AsyncReaderWriterLock AsyncReaderWriterLock)
{
_asyncReaderWriterLock=asyncReaderWriterLock;
}
公共空间处置()
{
_asyncReaderWriterLock.ReleaseWriterLock();
}
}
}
使用AsyncNameDrawerWriterLocker类的示例:
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);
}
}
}
公共类AsyncFileIO
{
私有静态只读AsyncNameDrawerWriterLocker AsyncNameDrawerWriterLocker=新的AsyncNameDrawerWriterLocker();
公共异步任务CreateFile(字符串文件名,字节[]数据)
{
使用(等待AsyncNameDrawerWriterLocker.EnterWriterLockAsync(文件名))
{
var directory=Path.GetDirectoryName(文件名);
如果(!Directory.Exists(Directory))
{
CreateDirectory(目录);
}
使用(var stream=File.Create(文件名))
{
wait stream.WriteAsync(数据,0,数据.长度);
}
}
}
公共异步任务读取文件(字符串文件名)
{
使用(等待AsyncNameDrawerWriterLocker.EnterReaderLockAsync(文件名))
{
如果(!File.Exists(filename))返回null;
使用(var stream=File.OpenRead(文件名))
{
var data=新字节[stream.Length];
wait stream.ReadAsync(数据,0,0);
返回数据;
}
}
}
公共异步任务删除文件(字符串文件名)
{
使用(等待AsyncNameDrawerWriterLocker.EnterWriterLockAsync(文件名))
{
var directoryName=Path.GetDirectoryName(文件名);
如果(!Directory.Exists(directoryName))返回;
删除(文件名);
}
}
}
请注意,出于学习目的,我对此非常感兴趣,并充分意识到这对于大多数应用程序来说都是过分的。起初,我在尝试同步时遇到了死锁
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);
}
}
}
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();
}
}
}