Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing to a file asynchronously

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();
    }
}
like image 656
CPK_2011 Avatar asked Aug 02 '12 09:08

CPK_2011


4 Answers

Well I had the same problem. And solved it now. It is kind of late suggestion but may be help for others.

Include the following using statements in the console examples below.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
Use of the FileStream Class

The examples below use the FileStream class, which has an option that causes asynchronous I/O to occur at the operating system level. In many cases, this will avoid blocking a ThreadPool thread. To enable this option, you must specify the useAsync=true or options=FileOptions.Asynchronous argument in the constructor call.

StreamReader and StreamWriter do not have this option if you open them directly by specifying a file path. StreamReader/Writer do have this option if you provide them a Stream that was opened by the FileStream class. Note that asynchrony provides a responsiveness advantage in UI apps even if a thread pool thread is blocked, since the UI thread is not blocked during the wait.

Writing Text

The following example writes text to a file. At each await statement, the method immediately exits. When the file I/O is complete, the method resumes at the statement following the await statement. Note that the async modifier is in the definition of methods that use the await statement.

static void Main(string[] args)
{
    ProcessWrite().Wait();
    Console.Write("Done ");
    Console.ReadKey();
}

static Task ProcessWrite()
{
    string filePath = @"c:\temp2\temp2.txt";
    string text = "Hello World\r\n";

    return WriteTextAsync(filePath, text);
}

static async Task WriteTextAsync(string filePath, string text)
{
    byte[] encodedText = Encoding.Unicode.GetBytes(text);

    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Append, FileAccess.Write, FileShare.None,
        bufferSize: 4096, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

Reading Text

The following example reads text from a file. The text is buffered and, in this case, placed into a StringBuilder. Unlike in the previous example, the evaluation of the await produces a value. The ReadAsync method returns a Task, so the evaluation of the await produces an Int32 value (numRead) that is returned after the operation completes..

static void Main(string[] args)
{
    ProcessRead().Wait();
    Console.Write("Done ");
    Console.ReadKey();
}

static async Task ProcessRead()
{
    string filePath = @"c:\temp2\temp2.txt";

    if (File.Exists(filePath) == false)
    {
        Console.WriteLine("file not found: " + filePath);
    }
    else {
        try {
            string text = await ReadTextAsync(filePath);
            Console.WriteLine(text);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

static async Task<string> ReadTextAsync(string filePath)
{
    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read,
        bufferSize: 4096, useAsync: true))
    {
        StringBuilder sb = new StringBuilder();

        byte[] buffer = new byte[0x1000];
        int numRead;
        while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            string text = Encoding.Unicode.GetString(buffer, 0, numRead);
            sb.Append(text);
        }

        return sb.ToString();
    }
} 

Original source was here but unfortunately the link seems dead now.

New source can be found here.

Hope that helps...

like image 142
curiousBoy Avatar answered Nov 18 '22 12:11

curiousBoy


Example of a helper method to handle async writing to a file.

public async Task FileWriteAsync(string filePath, string messaage, bool append = true)
    {
        using (FileStream stream = new FileStream(filePath, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
        using (StreamWriter sw = new StreamWriter(stream))
        {
            await sw.WriteLineAsync(messaage);
        }
    }
like image 38
SteveD Avatar answered Nov 18 '22 12:11

SteveD


Writing asynchronously to the file will not solve this issue. You'll need to wait for the file to be available.

like image 7
Emond Avatar answered Nov 18 '22 13:11

Emond


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
}
like image 1
nivs1978 Avatar answered Nov 18 '22 13:11

nivs1978