9

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.

wonea
  • 4,783
  • 17
  • 86
  • 139
Jurgen
  • 99
  • 1
  • 2
  • 1
    are you trying to implement a classic producer-consumer-scenario? give us more information on your scenario - it's very hard to work/help with that trimmed view on your actual problem! –  Jan 31 '12 at 10:02
  • 1
    any other software like Windows Explorer, Notepad etc. does NOT know anything about your "mutex-based lock" and will happily ignore it! – Yahia Jan 31 '12 at 10:03
  • @Yahia Yes, I know, but that is not the idea. I use this construct for thread synchronization within my program. If I don not want other programs to acces the file, I'll just use the ordinary filelocking mechanisms. – Jurgen Jan 31 '12 at 10:08
  • @Andreas Well, I have different threads in my code that will write/remove data to/from a file, which I synchronize like this. – Jurgen Jan 31 '12 at 10:10
  • 1
    @Jurgen you need to provide much more information then... you just need a mechanism to "lock" tiles inside your multi-threaded process ? what .NET version are you using ? – Yahia Jan 31 '12 at 10:11
  • named-mutexes include n consumers of these mutexes, which is definitely not the correct way to go. rather go for a solution which uses a proper ratio of consumers of your mutexes/locks and cores (if you are after multithreading!) –  Jan 31 '12 at 10:12
  • @yahia Yes, I need to lock files inside my code. using .net 3.5. in a way that in theory n threads may execute the DoSomethingsWithAFile function, but have to wait if the file is already being processed – Jurgen Jan 31 '12 at 10:14
  • once again: having n threads doing i/o and being blocked by mutexes will definitely not improve your performance! –  Jan 31 '12 at 10:15
  • @Jurgen any chance you could go with .NET 4 ? – Yahia Jan 31 '12 at 10:15
  • @Andreas what do you mean by 'n consumers' for a named mutex? – Jurgen Jan 31 '12 at 10:16
  • @Andreas the n is theoretically. In fact there is a maximum of 6 threads... ;-) – Jurgen Jan 31 '12 at 10:17
  • by consumers i mean completely independent threads. it's not that good idea to have n threads/consumsers waiting for a lock/mutex to do i/o. –  Jan 31 '12 at 10:17
  • @Yahia Yes .net 4.0 is an option. Why would you suggest that? – Jurgen Jan 31 '12 at 10:18
  • maximum number of 6 within your specific domain? well not that good idea either. you will not gain a significant performance-boost - if that's what you are after! –  Jan 31 '12 at 10:18
  • @Andreas aha I see. But let me add this than: the chance that a file is being written to is small, so most of the time the thread doesn't have to wait, only when a file is locked in the way I described. – Jurgen Jan 31 '12 at 10:20
  • @Jurgen see my answer below... – Yahia Jan 31 '12 at 10:22
  • @Andreas No, I'm not after a performance boost. I'd like to use the construct to avoid 'file is locked' exceptions due to my threads doing something with the file. When outside processed are locking the file, I'm fine with an 'file is locked' exception – Jurgen Jan 31 '12 at 10:23
  • @Jurgen see my anser below :) –  Jan 31 '12 at 10:24

3 Answers3

3

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);
            }
        }
    }
}
wonea
  • 4,783
  • 17
  • 86
  • 139
gabba
  • 2,815
  • 2
  • 27
  • 48
  • 2
    I think there is a problem with removing the entry from the dictionary. What if three clients call the method: the first creates the key and acquires the lock, the second gets the existing key and waits for the lock, the first finishes and removes the key and releases the lock, the second can start, a third comes in and creates a new key and acquires a new lock before the second has finished. Second and third are now running simultaneously. – Rupert Rawnsley Sep 23 '13 at 08:46
3

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:

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!

Yahia
  • 69,653
  • 9
  • 115
  • 144
1

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?

wonea
  • 4,783
  • 17
  • 86
  • 139