Writing to a file asynchronously

Viewed 68976

Is there any way to write an asynchronous function that writes to data to a file repeatedly.

I am getting the following error when I write asynchronous function

The process cannot access the file 'c:\Temp\Data.txt' because it is being used by another process

public void GoButton_Click(object sender, System.EventArgs e)
{
    IAsyncResult ar = DoSomethingAsync(strURL, strInput);
    Session["result"] = ar;
    Response.Redirect("wait1.aspx");
}

private IAsyncResult DoSomethingAsync(string strURL, string strInput)
{
    DoSomethingDelegate doSomethingDelegate = new DoSomethingDelegate(DoSomething);
    IAsyncResult ar = doSomethingDelegate.BeginInvoke(strURL, strInput, new AsyncCallback(MyCallback), null);
    return ar;
}

private delegate void DoSomethingDelegate(string strURL, string strInput);

private void MyCallback(IAsyncResult ar)
{
    AsyncResult aResult = (AsyncResult)ar;
    DoSomethingDelegate doSomethingDelegate = (DoSomethingDelegate)aResult.AsyncDelegate;
    doSomethingDelegate.EndInvoke(ar);
}

private void DoSomething(string strURL, string strInput)
{
    int i = 0;
    for (i = 0; i < 1000; i++)
    {
        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End); 
        m_streamWriter.WriteLine("{0} ", MethodCall(strURL, strInput));
        m_streamWriter.Flush();
        m_streamWriter.Close();
    }
}
7 Answers

If you use a simple StreamWriter, you could replace it with a simple class. No need for async/await. This is an example of writing a text file.

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;

class LogWriter : IDisposable
{
    private BlockingCollection<string> blockingCollection = new BlockingCollection<string>();
    private StreamWriter log = null;
    bool run = true;
    Task task = null;

    public LogWriter(string logFilePath)
    {
        log = new StreamWriter(logFilePath);
        task = Task.Run(() =>
        {
            while (run)
            {
                log.WriteLine(blockingCollection.Take());
            }
           });
    }

    public void WriteLine(string value)
    {
        blockingCollection.Add(value);
    }

    public void Dispose()
    {
        run = false;
        task.Dispose();
        log.Close();
        log.Dispose();
    }
}

To use it, do just like you would do with a StreamWriter:

using (var log = new LogWriter(logFileName))
{
    log.WriteLine("Hello world");
    // Code here that should not be blocked by writing to the file
}

Simple and straightforward solution:

using var file = new StreamWriter(path);
await file.WriteAsync(content);

The accepted answer has the common async pitfall - the buffers are not flushed async-ly. Check this out: https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#always-call-flushasync-on-streamwriters-or-streams-before-calling-dispose

Either use the new await using or flush the buffer manually before disposing

await using (var file = new StreamWriter(path)) //mind the "await using"
{
    await file.WriteAsync(content);
}

or

using (var streamWriter = new StreamWriter(context.Response.Body))
{
    await streamWriter.WriteAsync("Hello World");
    await streamWriter.FlushAsync();
}
Related