Alaa Mohammed
Alaa Mohammed

Reputation: 392

Reading file contents from many processes at the same time

I have a class library that gets called from a windows service, the class library can be called many times at the same time.

I have an issue where i have to read file contents in my class, so i get the error that the file is being used by another process if the class is getting called by many processes.

This is how i read the file content:

  File.ReadAllBytes("path");

What is the best solution in this case ?

Thank you

Upvotes: 0

Views: 1064

Answers (3)

György Kőszeg
György Kőszeg

Reputation: 18013

Use Mutex for syncing among different processes. File.ReadAllBytes uses a FileAccess.Read and FileShare.Read when reads the file, so normally you don't need to use any locks here. So you get this exception because the file is being written somewhere (or at least is locked for writing).

Solution 1 - if you are the one who writes this file

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

Remark: A ReadWriteLock would be better here because you can read a file safely parallelly when it is not being written; however, there is no built-in inter-process read-write lock in .NET. Here is an example how you can implement one with Mutex and Semaphor types.

Solution 2 - if you just read the file

You must simply being prepared that the file can be locked when it is being written by a 3rd process:

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
}

Upvotes: 1

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64933

You need to synchronize the access to the whole file using standard thread synchronization approaches.

The simplest one is Monitor using lock statement:

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

Note

Firstly, I was understanding OP was accessing the file from different processes, but when I double-checked the statement:

I have a class library that gets called from a windows service, the class library can be called many times at the same time.

...I realized OP is calling a method which reads all bytes from some file within the same Windows service instance.

Upvotes: 1

Halis S.
Halis S.

Reputation: 446

The following code demonstrates to access a file by setting its share permissions. The first using block creates and writes file, second and third using blocks access and read the file.

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

Upvotes: 1

Related Questions