I'm using a named mutex to lock access to a file (with path 'strFilePath') in a construction like this:
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();
}
}
So, this way the code will only block the thread when the same file is being processed already. Well, I tested this and seemed to work okay, but I really would like to know your thoughts about this.
Since you are talking about a producer-consumer situation with multiple threads the "standard solution would be to use BlockingCollection
which is part of .NET 4 and up - several links with information:
- http://msdn.microsoft.com/en-us/library/dd997371.aspx
- http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx
- http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/03/c.net-little-wonders-concurrentbag-and-blockingcollection.aspx
- http://www.albahari.com/threading/part5.aspx
IF you just want to make the locking process work then:
use a ConcurrentDictionary
in combination with the TryAdd
method call... if it returns true
then the file was not "locked" and is now "locked" so the thread can proceed - and "unlock" it by calling Remove
at the end... any other thread gets false
in the meantime and can decide what to do...
I would definitely recommend the BlockingCollection
approach though!
I ran into the same problem with many threads that can write in the same file.
The one of the reason that mutex not good because it slowly:
duration of call mutexSyncTest: 00:00:08.9795826
duration of call NamedLockTest: 00:00:00.2565797
BlockingCollection collection - very good idea, but for my case with rare collisions, parallel writes better than serial writes. Also way with dictionary much more easy to realise.
I use this solution (UPDATED):
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);
}
}
}
}
An alternative would be: make one consumer thread which works on a queue, and blocks if it is empty. You can have several producer threads adding several filepaths to this queue and inform the consumer.
Since .net 4.0 there's a nice new class: System.Collections.Concurrent.BlockingCollection<T>
A while ago I had the same issue here on Stack Overflow - How do I implement my own advanced Producer/Consumer scenario?
来源:https://stackoverflow.com/questions/9077573/using-a-named-mutex-to-lock-a-file