C# StreamWriter with using-block inside a while loop intermittently fails on slow network drive

54 Views Asked by At

I have a logging class in which I spawn a Task to append all messages from a BlockingCollection<string> to the log file, sleep two seconds, and repeat infinitely, until the fifo is completed.

This works fine for local files, but when writing to a slow network drive I'm getting intermittent failures at using (StreamWriter sw): IOException - The process cannot access the file 'x.txt' because it is being used by another process.

I am not writing to the same file from two threads. There are 3-4 instances of my logger going, but each instance has a unique filename based on timestamp and guid. No external apps are accessing these files at the time.

Even though the StreamWriter is disposed by the using-block in the first trip through the while loop and I sleep for a while, the second trip through the while loop somehow still finds the file to be open at some lower level (The stack trace gets down to System.IO.FileStream.Init, and ultimately, System.IO.__Error.WinIOError.

I can intermittently, sometimes, reproduce the exception in a simple integration test that spins up 100 logger objects in a Parallel.For loop.

        private void FlushFifoToLogFile()
        {
            while (!_fifoLog.IsCompleted)
            {
                if (_fifoLog.Count > 0)
                {
                    using (StreamWriter sw = new StreamWriter(_logFilePath, append:true))
                    {
                        while (_fifoLog.TryTake(out string line))
                        {
                            sw.WriteLine(line);
                        }
                    }
                }
                
                _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS);
            }
        }
1

There are 1 best solutions below

12
amonroejj On

Per recommendation from @Charlieface, the lowest friction solution is a basic try/catch.

I also incorporated @Steve's StringBuilder suggestion.

        private void FlushFifoToLogFile()
        {
            while (!_fifoLog.IsCompleted)
            {
                if (_fifoLog.Count > 0)
                {
                    try
                    {
                        Encoding utf8NoBom = new UTF8Encoding(false);
                        using (StreamWriter sw = new StreamWriter(_logFilePath, append:true, encoding:utf8NoBom, bufferSize:65536))
                        {
                            StringBuilder sb = new StringBuilder();
                            while (_fifoLog.TryTake(out string line))
                            {
                                sb.AppendLine(line);
                                if (_echoToConsole == true) { Console.WriteLine(line); }
                            }
                            sw.Write(sb.ToString());
                        }
                    }
                    catch (IOException)         // IOException is semi-expected
                    {
                        // Do nothing. Unprocessed items will still be waiting in the fifo for the next attempt.
                    }                            
                }
                
                _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS);
            }
        }