Give this a try. Setup a new Windows Forms application with a single button and the following code for the button click event:
private async void button1_Click(object sender, EventArgs e)
{
using (var file = File.OpenRead(@"C:\Temp\Sample.txt"))
{
byte[] buffer = new byte[4096];
int threadId = Thread.CurrentThread.ManagedThreadId;
int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
}
}
Then run the application and click the button rapidly. If your experience is anything like mine you’ll find that sometimes it works as expected, however, other times the Debug.Assert
will fail.
Based on my understanding of ConfigureAwait
as explained on Stephen Cleary’s blog, passing false
for continueOnCapturedContext
should instruct the task to not sync back to the “main” context (UI thread in this case) and that execution should continue on the thread pool.
Why then would the assert fail randomly? I can only assume that ReadAsync
will sometimes not complete on a background thread, i.e. it will complete synchronously.
This behavior is in line with KB 156932 - Asynchronous Disk I/O Appears as Synchronous on Windows:
Most I/O drivers (disk, communications, and others) have special case code where, if an I/O request can be completed "immediately," the operation will be completed and the ReadFile or WriteFile function will return TRUE. In all ways, these types of operations appear to be synchronous. For a disk device, typically, an I/O request can be completed "immediately" when the data is cached in memory.
Are my assumptions and test application correct? Can the ReadAsync
method sometimes complete synchronously? Is there a way to guarantee that execution will always continue on a background thread?
This issue is wreaking havoc on my application which uses COM objects that require me to know which thread is executing at all times.
Windows 7 64-bit, .NET 4.5.1
Can the ReadAsync method sometimes complete synchronously?
Yes. This is not uncommon due to buffers both at the OS level and within the .NET stream types.
Is there a way to guarantee that execution will always continue on a background thread?
If you always want code to execute on a thread pool thread, then use Task.Run
:
private async void button1_Click(object sender, EventArgs e)
{
using (var file = File.OpenRead(@"C:\Temp\Sample.txt"))
{
byte[] buffer = new byte[4096];
int threadId = Thread.CurrentThread.ManagedThreadId;
int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
await Task.Run(() =>
{
Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
}).ConfigureAwait(false);
}
}
ConfigureAwait
is an optimization hint, not a command to move to a background thread. ConfigureAwait
has no effect if the operation is already completed. In this case, await
follows a "fast path" that essentially means it continues synchronously, so any ConfigureAwait
hints are ignored.
If the file read completes synchronously before the call to await, I am fairly certain that none of the async compiler magic happens and execution continues synchronously on the calling thread. I would use Task.Run(() => file.ReadAsync(buffer, 0, buffer.Length))
to queue the work to the thread pool if I wanted to guarantee that it starts on a thread pool thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With