Reputation: 3127
What I know about async/await is that when the task completes, the continuation is run on the same context when the await was called, which would, in my case, be the UI thread. But my question is, does it create a new thread (internally) after IO is complete and before moving to the same UI thread.
I am sharing a piece of code. If I click on this button once, It shows that available thread is 1023 before executing await, but after that, available threads dropped to 1022. Although When I check the thread id, it is the same as UI thread.
private async void button1_ClickAsync(object sender, EventArgs e)
{
int x, y;
ThreadPool.GetAvailableThreads(out x, out y);
textBox1.Text = x.ToString()+"..."+y.ToString();
await Task.Delay(5000);
ThreadPool.GetAvailableThreads(out x, out y);
textBox1.Text = x.ToString() + "..." + y.ToString();
}
But interestingly, next time when I click on this button, number of available threads remains 1023 (before and after await).
Upvotes: 8
Views: 900
Reputation: 456507
But my question is, does it create a new thread (internally) after IO is complete and before moving to the same UI thread.
Other threads may be temporarily used, but you don't need to worry about that.
In particular, I/O on .NET generally goes through an I/O completion port that is part of the thread pool. I/O threads are automatically added and removed as necessary. Fairly often, the I/O has some additional work to do before it actually is ready to return to your code (e.g., parsing HTTP response headers), so a lot of the BCL I/O code will actually use the I/O thread just to queue work to the thread pool. So a thread pool worker thread is often used (briefly) by I/O code.
Also, in this particular example, I believe there's a separate timer thread as well, that coalesces system timers. Naturally, this is an implementation detail and subject to change.
So, in summary, other threads may be created/destroyed/temporarily used, but you don't have to worry about it. They're all managed by the BCL or .NET runtime in a very efficient manner, striking a balance between reusing threads (minimizing churn) and minimizing resource use (especially memory).
Upvotes: 5
Reputation: 15413
I am guessing you meant it dropped to 1022.
In general I think it depends on the async call being made. Disk and network calls will come back on a thread from the I/O completion thread pool. It appears that Task.Delay
returns on a regular worker thread.
You can change the line to await Task.Delay(5000).ConfigureAwait(false);
, set a breakpoint after it and check the Threads window to see this directly.
It will complete on a worker thread no matter where you call it from. await
doesn't communicate the calling context to the function implementing the async operation; it only adds the extra step of returning to the UI thread when it's done.
I wouldn't read too much into tracking the exact number here; the CLR has its own algorithms that manage the thread pool size and those can change from release to release. And I wouldn't stress about it using a different thread there: in a normal app it will simply re-use an existing thread from the pool and the operation will be very fast.
Upvotes: 0