Async

Async reduces the need for threads in server-based applications. If the app uses a dedicated thread per response and 1,000 requests are handled simultaneously, 1,000 threads are needed. Async operations don’t need to use a thread during the wait. They use the existing I/O completion thread briefly at the end.

Asynchronous I/O – Writing

Write Example

public async Task SimpleWriteAsync() {
    string filePath = "simple.txt";
    string text = $"Hello World";

    await File.WriteAllTextAsync(filePath, text);
}

Fine Control

For fine control, use the FileStream class with options=FileOptions.Asynchronous or useAsync: true in the constructor call instead of using File.WriteAllTextAsync/File.ReadAllTextAsync.

  • This causes asynchronous I/O to occur at the OS level, which avoids blocking a thread pool thread.

If using StreamReader or StreamWriter, provide a Stream that FileStream opened.

public async Task ProcessWriteAsync() {
    string filePath = "temp.txt";
    string text = $"Hello World{Environment.NewLine}";

    await WriteTextAsync(filePath, text);
}

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

    using var sourceStream =
        new FileStream(
            filePath,
            FileMode.Create, FileAccess.Write, FileShare.None,
            bufferSize: 4096, useAsync: true);

    await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}

Asynchronous I/O – Reading

Read Example

public async Task SimpleReadAsync() {
    string filePath = "simple.txt";
    string text = await File.ReadAllTextAsync(filePath);

    Console.WriteLine(text);
}

Fine Control

public async Task ProcessReadAsync() {
    try {
        string filePath = "temp.txt";
        if (File.Exists(filePath) != false) {
            string text = await ReadTextAsync(filePath);
            Console.WriteLine(text);
        }
        else {
            Console.WriteLine($"file not found: {filePath}");
        }
    } catch (Exception ex) {
        Console.WriteLine(ex.Message);
    }
}

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

    var sb = new StringBuilder();

    byte[] buffer = new byte[0x1000];
    int numRead;
    // Here, ReadAsync returns a Task<Int32>, so the await evaluation produces an Int32 value stored in 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();
}