I'm reading through the Exam Ref 70-483: Programming in C# book and the following code sample is given:
public Task SleepAsyncA(int millisecondsTimeout)
{
return Task.Run(() => thread.Sleep(millisecondsTimeout);
}
public Task SleepAsyncB(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, -1, -1);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}
The paragraph under states this:
The
SleepAsyncA
method uses a thread from the thread pool while sleeping. the second method, however, which has a completely different implementation, does not occupy a thread while waiting for the timer to run. The second method gives you scalability.
Why is A responsive but B scalable?
Assuming the starting point would have no process/thread/task control and be a non-responsive spinning loop, checking if the time has passed e.g.:
public void SleepBadly(int millisecondsTimeout)
{
var stopTime = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);
while (DateTime.UtcNow < stopTime) {}
return;
}
SleepAsyncA sleeps the thread rather than spinning so it doesn't use any CPU, so would be responsive as the CPU is available, but it is still using the thread while it sleeps.
SleepAsyncB gives up the thread while it waits, so it doesn't use CPU and that thread can be used for something else; so it is responsive and scalable.
For example, at scale, if you had 100,000 calls outstanding in SleepAsyncA; either you would have exhausted the threadpool and they would start queuing or you would have 100,000 active threads, neither of which is very good for scalability.
SleepAsyncB on the other hand would be using 0 threads while the 100,000 calls were waiting and doing nothing is infinitely more scalable than doing something.
However, while SleepAsyncB is a good example of how to use Task constructs like TaskCompletionSource, what you'd probably want to actually do in this example is:
public Task SleepAsyncC(int millisecondsTimeout)
{
return Task.Delay(millisecondsTimeout);
}
A is responsive because it has the appearance of asynchronicity by not blocking a thread which is important to the user. The UI remains fluid, but it is not scalable, because under the hood it ties up a limited resource (by blocking a thread).
B is still responsive, but also scalable, because it is truly asynchronous rather than merely giving the appearance of being so. It does not tie up any limited resources, even as the UI remains fluid.
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