【问题标题】:Reading file contents from many processes at the same time同时从多个进程中读取文件内容
【发布时间】:2015-11-20 02:47:17
【问题描述】:

我有一个从 Windows 服务调用的类库,该类库可以同时调用多次。

我有一个问题,我必须在我的类中读取文件内容,因此如果该类被许多进程调用,我会收到另一个进程正在使用该文件的错误。

这就是我读取文件内容的方式:

  File.ReadAllBytes("path");

在这种情况下最好的解决方案是什么?

谢谢

【问题讨论】:

  • 使用lock机制
  • File.ReadAllBytes 实际上应该在许多线程和许多进程中工作。您是否在代码的其他位置打开相同的文件(用于读取或写入)?还是在另一个进程中?
  • 这个文件(我从中读取)是在类库第一次运行时生成的。之后,我将从该文件中读取并且永远不会写入它。

标签: c#


【解决方案1】:

您需要使用标准线程同步方法来同步对整个文件的访问。

最简单的一种是Monitor 使用lock 语句:

public class A
{
    private static readonly object _sync = new object();

    public void DoStuff() 
    {
          // All threads trying to enter this critical section will
          // wait until the first to enter exits it
          lock(_sync)
          {
              byte[] buffer = File.ReadAllBytes(@"C:\file.jpg");
          }
    }
}

注意

首先,我理解 OP 是从不同进程访问文件,但是当我仔细检查声明时:

我有一个从 Windows 服务调用的类库,即 类库可以同时调用多次。

...我意识到 OP 正在调用一个方法,该方法从同一个 Windows 服务实例中的某个文件中读取所有字节。

【讨论】:

  • 这不起作用,因为锁是私有的,每个process。但他需要一个系统范围的锁。
  • @CSharper 我对此不确定。我最初的答案是使用Mutex,但由于 OP 声明该库在同一个 Windows 服务中被多次使用...
  • 如果这是真的,那么你的答案是正确的。
  • @CSharper 我认为这应该是线程问题,而不是多进程问题。 “Windows 服务”说明了问题...
  • ILSpy 显示File.ReadAllBytes 已经使用FileStreamFileShare.Read
【解决方案2】:

以下代码演示如何通过设置共享权限来访问文件。第一个using 块创建和写入文件,第二个和第三个using 块访问和读取文件。

var fileName = "test.txt";
using (var fsWrite = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
{
    var content = Encoding.UTF8.GetBytes("test");

    fsWrite.Write(content, 0, content.Length);
    fsWrite.Flush();

    using (var fsRead_1 = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        var bufRead_1 = new byte[fsRead_1.Length];
        fsRead_1.Read(bufRead_1, 0, bufRead_1.Length);
        Console.WriteLine("fsRead_1:" + Encoding.UTF8.GetString(bufRead_1));

        using (var fsRead_2 = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            var bufRead_2 = new byte[fsRead_2.Length];
            fsRead_2.Read(bufRead_2, 0, bufRead_2.Length);
            Console.WriteLine("fsRead_2:" + Encoding.UTF8.GetString(bufRead_2));
        }
    }
}

【讨论】:

  • 你的回答不是正好说明了我提供了什么吗?
  • 存在细微差别。我上面的代码不适用于FileShare.Read,但不适用于FileShare.ReadWrite。我试图实现最坏的情况(在写入时读取文件)
【解决方案3】:

使用Mutex 在不同进程之间进行同步。 File.ReadAllBytes 在读取文件时使用FileAccess.ReadFileShare.Read,所以通常你不需要在这里使用任何锁。所以你得到这个异常是因为文件正在某处写入(或者至少被锁定以进行写入)。

解决方案 1 - 如果您是编写此文件的人

private static Mutex mutex;

public void WriteFile(string path)
{
    Mutex mutex = GetOrCreateMutex();
    try
    {
        mutex.WaitOne();
        // TODO: ... write file
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

public byte[] ReadFile(string path)
{
    // Note: If you just read the file, this lock is completely unnecessary
    // because ReadAllFile uses Read access. This just protects the file
    // being read and written at the same time
    Mutex mutex = GetOrCreateMutex();
    try
    {
        mutex.WaitOne();
        return File.ReadAllBytes(path);
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

private static Mutex GetOrCreateMutex()
{
    try
    {
        mutex = Mutex.OpenExisting("MyMutex");
    }
    catch (WaitHandleCannotBeOpenedException)
    {
        mutex = new Mutex(false, "MyMutex");
    }
}

备注:ReadWriteLock 在这里会更好,因为您可以在文件未写入时安全地并行读取文件;但是,.NET 中没有内置的进程间读写锁。 Here is an example 如何使用 MutexSemaphor 类型实现一个。

解决方案 2 - 如果您只是阅读文件

您必须做好准备,当文件被第三个进程写入时,文件可能会被锁定:

public byte[] TryReadFile(string path, int maxTry)
{
    Exception e;

    for (int i = 0; i < maxTry; i++)
    {
        try
        {
            return File.ReadAllBytes(path);
        }
        catch (IOException io)
        {
            e = io;
            Thread.Sleep(100);
        }
    }

    throw e; // or just return null
}

【讨论】:

    猜你喜欢
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 2015-01-05
    • 1970-01-01
    • 2011-04-01
    • 2018-01-20
    • 1970-01-01
    相关资源
    最近更新 更多