Stream vs async stream behavior in C Sharp (C#) - Performance Comparison
When working with streams in C#, it's important to understand how time grows when reading data synchronously versus asynchronously.
We want to see how the program's running time changes as the amount of data increases.
Analyze the time complexity of the following code snippet.
public async Task ReadStreamAsync(Stream stream, int bufferSize)
{
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
// Process bytesRead
}
}
This code reads data from a stream asynchronously in chunks until all data is read.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Reading chunks of data repeatedly from the stream.
- How many times: The loop runs once for each chunk until the entire stream is read.
As the total data size grows, the number of read operations grows proportionally.
| Input Size (bytes) | Approx. Read Calls |
|---|---|
| 10,000 | 100 (if bufferSize=100) |
| 100,000 | 1,000 |
| 1,000,000 | 10,000 |
Pattern observation: The number of read calls grows linearly with the input size.
Time Complexity: O(n)
This means the time to read the stream grows directly in proportion to the amount of data.
[X] Wrong: "Async reading makes the operation faster and changes the time complexity."
[OK] Correct: Async reading helps with responsiveness but does not reduce the total amount of work, so time complexity remains linear.
Understanding how synchronous and asynchronous streams behave helps you explain performance in real applications and shows you can reason about program efficiency clearly.
"What if we doubled the buffer size? How would the time complexity change?"
Practice
Stream and an async stream in C#?Solution
Step 1: Understand Stream behavior
AStreamreads or writes data step-by-step synchronously, blocking the thread until data is processed.Step 2: Understand async stream behavior
Anasync streamusesIAsyncEnumerable<T>andawait foreachto process data asynchronously, allowing other work while waiting for data.Final Answer:
AStreamprocesses data synchronously, while anasync streamprocesses data asynchronously. -> Option AQuick Check:
Sync vs async processing = C [OK]
- Thinking async streams are only for writing data
- Confusing synchronous with asynchronous processing
- Believing streams only handle text or binary exclusively
Solution
Step 1: Recall async stream consumption syntax
Async streams requireawait foreachto asynchronously iterate overIAsyncEnumerable<T>.Step 2: Check each option
foreach (var item in asyncStream) { Console.WriteLine(item); } uses regularforeachwhich is invalid for async streams. await foreach (var item in asyncStream) { Console.WriteLine(item); } correctly usesawait foreach. for await (var item in asyncStream) { Console.WriteLine(item); } uses invalid syntaxfor await. async foreach (var item in asyncStream) { Console.WriteLine(item); } uses invalid keyword orderasync foreach.Final Answer:
await foreach (var item in asyncStream) { Console.WriteLine(item); } -> Option BQuick Check:
Async stream needs await foreach = A [OK]
- Using regular foreach with async streams
- Swapping 'async' and 'await' keywords incorrectly
- Trying 'for await' which is invalid in C#
async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 1; i <= 3; i++)
{
await Task.Delay(100);
yield return i;
}
}
static async Task Main()
{
await foreach (var num in GetNumbersAsync())
{
Console.Write(num + " ");
}
}
What will be the output when this program runs?Solution
Step 1: Understand async stream generation
The method yields numbers 1 to 3 with a 100ms delay between each, simulating asynchronous data arrival.Step 2: Understand async stream consumption
Theawait foreachprints each number followed by a space as they arrive asynchronously.Final Answer:
1 2 3 -> Option CQuick Check:
Async yield with await foreach prints spaced numbers = A [OK]
- Expecting numbers without spaces
- Thinking output is immediate without delay
- Confusing async stream with synchronous enumeration
async IAsyncEnumerable<int> GetDataAsync()
{
for (int i = 0; i < 3; i++)
{
yield return i;
await Task.Delay(100);
}
}Solution
Step 1: Understand async stream yield rules
In async streams,awaitmust appear beforeyield returnbecause the compiler needs to await before producing the next value.Step 2: Analyze the code order
The code yields a value first, then awaits delay, which is invalid and causes a compile error.Final Answer:
Theawaitmust come beforeyield returnin async streams. -> Option AQuick Check:
Await before yield return in async streams = B [OK]
- Placing yield return before await in async streams
- Confusing return types for async streams
- Assuming no await is needed in async streams
Solution
Step 1: Identify UI thread blocking issue
Reading large files synchronously blocks the UI thread, causing freezes.Step 2: Choose async streaming approach
UsingIAsyncEnumerable<string>withawait foreachreads lines asynchronously, allowing UI responsiveness.Final Answer:
UseIAsyncEnumerable<string>withawait foreachreading lines asynchronously. -> Option DQuick Check:
Async streams keep UI responsive during file read = D [OK]
- Reading files synchronously causing UI freeze
- Loading entire file into memory unnecessarily
- Using blocking Stream methods on UI thread
Ready to prove it?
Pass the test and this pattern moves unknown
