简体   繁体   中英

StreamWriter/Reader reading and then immediately writing causes exception

I need to read a number from a file and then increment the number and update the same file just for debugging purposes. I wrote this code and to my surprise it throws an exception (it is just for debugging but nonetheless I would like to get to the bottom of this):

The process cannot access the file '' because it is being used by another process.

Code:

string path = Server.MapPath("~/Log.txt");

int newTotal = 0;

using (var reader = new StreamReader(path))
{
    var total = reader.ReadLine();

    if (total != null)
    {
        newTotal = int.Parse(total) + 1;
    }
}

using (var writer = new StreamWriter(path, false))
{
    // I added AutoFlush because I read somewhere on SO 
    // this will write to disk immediately
    writer.AutoFlush = true; 
    writer.Write(newTotal);
}

Then I wanted to narrow the issue down to either reading or writing, so I did this:

string path = Server.MapPath("~/Log.txt");

int newTotal = 0;

try
{
    using (var reader = new StreamReader(path))
    {
        var total = reader.ReadLine();

        if (total != null)
        {
            newTotal = int.Parse(total) + 1;
        }
    }
}
catch (Exception)
{
    throw new System.Web.HttpException("During reading...");
}

try
{
    using (var writer = new StreamWriter(path, false))
    {
        writer.AutoFlush = true;
        writer.Write(newTotal);
    }
}
catch (Exception)
{
    throw new System.Web.HttpException("During writing...");
}

I am getting this so this tells me it happens during both but randomly:

During reading...
During writing...
During reading...
During writing...
During reading...
During reading...
During writing...
During writing...
During writing...
During writing...
During writing...
During reading...
During reading...
During reading...

Just a note that this code is in the Controller of an MVC app within this method:

protected override void Initialize(RequestContext requestContext)
{
    base.Initialize(requestContext);
    // Code is here
}

I used Process Explorer and nothing else is using the file.

Question : I am disposing things here so why the exception?

At first I thought perhaps there is a delay in disposing the reader but in that case all exceptions should be about reader but this is not the case. Also, below will also give the same exception:

this.path = Server.MapPath("~/Log.txt");

var total = System.IO.File.ReadAllText(this.path);
int newTotal = 0;

if (total != null)
{
    newTotal = int.Parse(total) + 1;
}

System.IO.File.WriteAllText(this.path, newTotal.ToString());

There are no threads. Just HTTP GET (regular and via AJAX).

EDIT

This happens during load testing using Visual Studio 2015.

I've run into this problem and I solved it by adding a while loop.

Try doing this and see if it solves your problem.

using (var reader = new StreamReader(path))
{
    while (reader.Peek() > -1) 
    {
        var total = reader.ReadLine();
        newTotal = int.Parse(total) + 1;
    }
}

The issue is NOT because of:

  1. GC not deallocating and the code reaching writing before reader is collected. I tested the exact code with a console application and the issue cannot be replicated.
  2. This is not due to threading ( except for one caveat ) because I am not creating any threads. Within ASP.NET each request gets assigned a thread from the pool and an instance of the class with be created for each thread 1 . If there are class level things ( static ), then they obviously will not be threadsafe. They will be shared. But this code is not within a static context.

Here is the caveat:

When using a browser, this issue was never replicated. I also did something simple like this:

protected override void Initialize(RequestContext requestContext)
{
     System.IO.File.AppendAllText(tPath, "Thread enter and ID is: " + 
         System.Threading.Thread.CurrentThread.ManagedThreadId + Environment.NewLine);
     // Placed all code here
     System.IO.File.AppendAllText(tPath, "Thread exit and ID is: " + 
         System.Threading.Thread.CurrentThread.ManagedThreadId + Environment.NewLine);
}

Now with the above code, when the requests are made through a browser, as expected, the contents of the file are as shown below. Notice every entry is synchronous :

Thread enter and ID is: 35
Thread exit and ID is: 35
Thread enter and ID is: 11
Thread exit and ID is: 11
Thread enter and ID is: 29
Thread exit and ID is: 29
Thread enter and ID is: 36
Thread exit and ID is: 36
Thread enter and ID is: 27
Thread exit and ID is: 27
Thread enter and ID is: 11
Thread exit and ID is: 11
Thread enter and ID is: 29
Thread exit and ID is: 29
Thread enter and ID is: 8
Thread exit and ID is: 8
Thread enter and ID is: 11
Thread exit and ID is: 11
Thread enter and ID is: 36
Thread exit and ID is: 36
...

But during Visual Studio load testing, the contents are like this and the entry into the method is not synchronous :

Thread enter and ID is: 12
Thread exit and ID is: 12
Thread enter and ID is: 29
Thread enter and ID is: 49
Thread enter and ID is: 51
Thread exit and ID is: 29
Thread exit and ID is: 51
Thread enter and ID is: 48
Thread exit and ID is: 48
Thread enter and ID is: 57
Thread exit and ID is: 57
Thread enter and ID is: 17
Thread exit and ID is: 17
Thread enter and ID is: 55
Thread exit and ID is: 55
Thread enter and ID is: 42
Thread exit and ID is: 42
Thread enter and ID is: 47
Thread enter and ID is: 55
...

Conclusion

In conclusion, when doing a load test with Visual Studio, the call behavior is not the exact same as it would be from a browser. For some reason, each thread is not given its own instance of the class. I have no idea why but if someone does, please chime in. I think this is bullcrap if you ask me: How can I do a load test if it is doing something which is totally against the ASP.NET architecture?

1. Thread safety ASP.NET

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM