.NET异步中的可伸缩性与响应性

时间:2013-12-30 22:33:43

标签: c# .net multithreading async-await scalability

我正在阅读Exam Ref 70-483: Programming in C#一书,并提供了以下代码示例:

上市1-19

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;
}

以下段落陈述:

  

SleepAsyncA方法使用线程池中的线程   睡眠。然而,第二种方法是完全的   不同的实现,在等待时不占用线程   要运行的计时器。第二种方法为您提供了可扩展性。

为什么A响应但B可扩展?

2 个答案:

答案 0 :(得分:9)

假设起始点没有进程/线程/任务控制并且是无响应的旋转循环,检查时间是否已经过去,例如:

public void SleepBadly(int millisecondsTimeout)
{
    var stopTime = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout);
    while (DateTime.UtcNow < stopTime) {}
    return;
}

SleepAsyncA睡眠线程而不是旋转所以它不使用任何CPU,所以响应因为CPU 可用,但它仍在使用线程在它睡觉的时候。

SleepAsyncB在等待时放弃线程,因此它不使用CPU,并且该线程可用于其他内容;所以它响应 可扩展

例如,在规模上,如果您在SleepAsyncA中有100,000个未完成的呼叫;要么你已经耗尽了线程池,他们会开始排队,或者你会有100,000个活动线程,这两个都不是很好的可伸缩性。

另一方面,SleepAsyncB将使用0个线程,而100,000个调用正在等待,什么都不做,比做某事更具可扩展性。

然而,虽然SleepAsyncB是如何使用TaskCompletionSource之类的Task结构的一个很好的例子,但你在这个例子中可能想要实际做的是:

public Task SleepAsyncC(int millisecondsTimeout)
{
    return Task.Delay(millisecondsTimeout);
}

答案 1 :(得分:6)

A 响应因为它没有阻止对用户重要的线程而具有异步性的外观。用户界面仍然流畅,但它不具备可扩展性,因为它会占用有限的资源(通过阻止线程)。

B仍然响应,但可扩展,因为它是真正的异步而不仅仅是外观如此。它不会占用任何有限的资源,即使UI仍然流畅。