我正在使用命名互斥体来锁定对文件(路径为“strFilePath”)的访问,如下所示:
private void DoSomethingsWithAFile(string strFilePath)
{
Mutex mutex = new Mutex(false,strFilePath.Replace("\\",""));
try
{
mutex.WaitOne();
//do something with the file....
}
catch(Exception ex)
{
//handle exception
}
finally
{
mutex.ReleaseMutex();
}
}
因此,这样代码只会在已经处理同一个文件时阻塞线程。 嗯,我测试了这个,似乎工作正常,但我真的很想知道你对此的想法。
我遇到了同样的问题,许多线程可以在同一个文件中写入。
互斥量不好的原因之一是它速度慢:
duration of call mutexSyncTest: 00:00:08.9795826
duration of call NamedLockTest: 00:00:00.2565797
BlockingCollection 集合 - 非常好的主意,但对于我的很少发生冲突的情况,并行写入比串行写入更好。另外,使用字典的方式更容易实现。
我使用这个解决方案(更新):
public class NamedLock
{
private class LockAndRefCounter
{
public long refCount;
}
private ConcurrentDictionary<string, LockAndRefCounter> locksDictionary = new ConcurrentDictionary<string, LockAndRefCounter>();
public void DoWithLockBy(string key, Action actionWithLock)
{
var lockObject = new LockAndRefCounter();
var keyLock = locksDictionary.GetOrAdd(key, lockObject);
Interlocked.Increment(ref keyLock.refCount);
lock (keyLock)
{
actionWithLock();
Interlocked.Decrement(ref keyLock.refCount);
if (Interlocked.Read(ref keyLock.refCount) <= 0)
{
LockAndRefCounter removed;
locksDictionary.TryRemove(key, out removed);
}
}
}
}
既然您正在谈论具有多个线程的生产者-消费者情况,“标准解决方案是使用
BlockingCollection
,它是 .NET 4 及更高版本的一部分 - 几个包含信息的链接:
如果您只想让锁定过程正常工作,那么:
ConcurrentDictionary
与 TryAdd
方法调用结合使用...如果它返回 true
那么文件没有“锁定”,现在“锁定”,因此线程可以继续 - 并“解锁”通过在最后调用 Remove
来实现...任何其他线程同时获得 false
并可以决定要做什么...
我绝对会推荐
BlockingCollection
方法!
另一种选择是:创建一个在队列上工作的消费者线程,如果队列为空则阻塞。您可以让多个生产者线程向此队列添加多个文件路径并通知消费者。
自 .net 4.0 以来,出现了一个不错的新类:
System.Collections.Concurrent.BlockingCollection<T>
不久前,我在 Stack Overflow 上遇到了同样的 问题 - 如何实现我自己的高级生产者/消费者场景?
对于像我这样将来偶然发现这个线程的人 - @gabba 给出的答案非常有效(通过从 512 个并发线程访问同一文件并执行写入、读取和文件删除进行测试),但是如果计算引用不重要,代码如下:
public class NamedLock
{
private class LockAndRefCounter
{
public long refCount;
}
private ConcurrentDictionary<string, LockAndRefCounter> locksDictionary = new ConcurrentDictionary<string, LockAndRefCounter>();
public void DoWithLockBy(string key, Action actionWithLock)
{
var lockObject = new LockAndRefCounter();
var keyLock = locksDictionary.GetOrAdd(key, lockObject);
Interlocked.Increment(ref keyLock.refCount);
lock (keyLock)
{
actionWithLock();
Interlocked.Decrement(ref keyLock.refCount);
if (Interlocked.Read(ref keyLock.refCount) <= 0)
{
LockAndRefCounter removed;
locksDictionary.TryRemove(key, out removed);
}
}
}
}
可以简化为:
public class NamedLock
{
private class LockObject { /* Empty */ }
private static readonly ConcurrentDictionary<string, LockObject> locks = new();
public void DoWithLockBy(string key, Action actionWithLock)
{
LockObject _lock = locks.GetOrAdd(key, new LockObject());
lock (_lock)
{
action();
}
}
}
结果是每个文件路径的空对象 - 即“命名”锁。
lock
语句不会将锁定对象从字典中删除,而是用作访问此文件路径的所有线程的互斥体。