使用命名互斥锁来锁定文件

9

我正在使用一个命名互斥锁来锁定对文件(路径为 '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();
      }
 }

因此,这种方法只有在处理相同文件时才会阻塞线程。我测试了一下,似乎可以正常工作,但我真的很想知道你对此的想法。


1
你是在尝试实现一个经典的生产者-消费者场景吗?请给我们更多关于你的场景信息 - 只有那种简略的视图,很难处理/帮助你解决实际问题! - user57508
1
任何其他软件,如Windows资源管理器、记事本等都不知道你的“互斥锁”是什么,并且会快乐地忽略它! - Yahia
@Yahia 是的,我知道,但那不是我的想法。我在程序中使用这个结构来进行线程同步。如果我不希望其他程序访问该文件,我将只使用普通的文件锁定机制。 - Jurgen
@Andreas 嗯,我的代码中有不同的线程会写入/删除文件中的数据,我是这样进行同步的。 - Jurgen
1
@Jurgen,你需要提供更多的信息...你只需要一个机制来在多线程过程中“锁定”瓷砖吗?你使用的是哪个.NET版本? - Yahia
显示剩余13条评论
3个回答

3

我遇到了许多线程可以写入同一个文件的相同问题。

互斥锁不好的原因之一是它很慢:

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);
            }
        }
    }
}

2
我认为从字典中删除条目存在问题。如果有三个客户端调用该方法会怎样:第一个客户端创建了键并获取锁,第二个客户端获取现有键并等待锁,第一个客户端完成并删除键并释放锁,第二个客户端开始运行,第三个客户端进入并创建新的键并获取新的锁,而第二个客户端还未完成。第二和第三个客户端现在同时运行。 - Rupert Rawnsley

3

由于你谈论的是具有多个线程的生产者-消费者情况,因此“标准解决方案”将使用 BlockingCollection,它是 .NET 4 及以上版本的一部分 - 这里提供了多个信息链接:

如果你只想让锁定过程正常工作,那么:请使用ConcurrentDictionaryTryAdd方法结合使用... 如果它返回true,那么文件没有被“锁定”,现在已经“锁定”,因此线程可以继续-并在最后通过调用Remove来“解锁”它... 在此期间,任何其他线程都会得到false,并可以决定该怎么做...我肯定会推荐使用BlockingCollection方法!

达...你比我快了4秒钟... :) - user57508

1
另一种选择是:创建一个消费者线程,它在队列上工作,并在队列为空时阻塞。您可以有多个生产者线程将多个文件路径添加到此队列并通知消费者。
自从 .net 4.0 以来,有一个很好的新类:System.Collections.Concurrent.BlockingCollection<T> 不久前我在 Stack Overflow 上遇到了同样的 问题 - 如何实现自己的高级生产者/消费者场景?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接