gpt4 book ai didi

c# - TaskContinuationOptions.RunContinuationsAsynchronously 和 Stack Dives

转载 作者:IT王子 更新时间:2023-10-29 04:49:59 31 4
gpt4 key购买 nike

this blog post , Stephan Toub 描述了将包含在 .NET 4.6 中的一项新功能,它向 TaskCreationOptions 和 TaskContinuationOptions 枚举添加了另一个值,称为 RunContinuationsAsynchronously

他解释说:

"I talked about a ramification of calling {Try}Set* methods onTaskCompletionSource, that any synchronous continuations offof the TaskCompletionSource’s Task could run synchronously aspart of the call. If we were to invoke SetResult here while holdingthe lock, then synchronous continuations off of that Task would be runwhile holding the lock, and that could lead to very real problems.So, while holding the lock we grab the TaskCompletionSource tobe completed, but we don’t complete it yet, delaying doing so untilthe lock has been released"

并给出如下例子进行演示:

private SemaphoreSlim _gate = new SemaphoreSlim(1, 1);
private async Task WorkAsync()
{
await _gate.WaitAsync().ConfigureAwait(false);
try
{
// work here
}
finally { _gate.Release(); }
}

Now imagine that you have lots of calls to WorkAsync:

await Task.WhenAll(from i in Enumerable.Range(0, 10000) select WorkAsync());

We've just created 10,000 calls to WorkAsync that will beappropriately serialized on the semaphore. One of the tasks willenter the critical region, and the others will queue up on theWaitAsync call, inside SemaphoreSlim effectively enqueueing the taskto be completed when someone calls Release. If Release completed thatTask synchronously, then when the first task calls Release, it'llsynchronously start executing the second task, and when it callsRelease, it'll synchronously start executing the third task, and soon. If the "//work here" section of code above didn't include anyawaits that yielded, then we're potentially going to stack dive hereand eventually potentially blow out the stack.

我很难理解他所说的同步执行延续的部分。

问题

这怎么可能导致堆栈跳水?更重要的是,为了解决该问题,RunContinuationsAsynchronously 将有效做什么?

最佳答案

这里的关键概念是任务的延续可以在完成先前任务的同一线程上同步运行。

让我们假设这是 SemaphoreSlim.Release 的实现(它实际上是 Toub 的 AsyncSemphore 的):

public void Release() 
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}

我们可以看到它同步完成了一个任务(使用TaskCompletionSource)。在这种情况下,如果 WorkAsync 没有其他异步点(即根本没有 await,或者所有 await 都在一个已经完成的任务上) 并调用 _gate.Release() 可能会在同一线程上同步完成对 _gate.WaitAsync() 的挂起调用,您可能会达到单个线程顺序释放的状态信号量,完成下一个挂起的调用,执行 //work here 然后再次释放信号量等等。

这意味着同一个线程在堆栈中越来越深,因此堆栈潜水。

RunContinuationsAsynchronously 确保延续不会同步运行,因此释放信号量的线程继续移动,并且为另一个线程安排延续(哪个线程取决于其他延续参数,例如 任务调度器)

这在逻辑上类似于将完成发布到 ThreadPool:

public void Release() 
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
Task.Run(() => toRelease.SetResult(true));
}

关于c# - TaskContinuationOptions.RunContinuationsAsynchronously 和 Stack Dives,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28321457/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com