Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在Task.Run中进行包装会从asp.net core中移除死锁?_C#_Asynchronous_Asp.net Core - Fatal编程技术网

C# 为什么在Task.Run中进行包装会从asp.net core中移除死锁?

C# 为什么在Task.Run中进行包装会从asp.net core中移除死锁?,c#,asynchronous,asp.net-core,C#,Asynchronous,Asp.net Core,我正在尝试修复一个方法,该方法应该是使用内置的GetOrCreateAsync从MemoryCache检索任意数据。以下是Microsofts的示例(来自): 并且EventsForDay中的所有异步操作都是await和ConfigureAwait(false),以尝试防止死锁。然而,即使使用那些ConfigureAwait(false),上面的异步lambda仍然会死锁并且永远不会返回 但是,如果我使用此处详述的解决方案:它不再死锁: /// <summary> /// This

我正在尝试修复一个方法,该方法应该是使用内置的
GetOrCreateAsync
MemoryCache
检索任意数据。以下是Microsofts的示例(来自):

并且
EventsForDay
中的所有异步操作都是
await
ConfigureAwait(false)
,以尝试防止死锁。然而,即使使用那些
ConfigureAwait(false)
,上面的异步lambda仍然会死锁并且永远不会返回

但是,如果我使用此处详述的解决方案:它不再死锁:

/// <summary>
/// This does not deadlock. Credit here: https://stackoverflow.com/a/40569410
/// </summary>
/// <param name="dayKey"></param>
/// <returns></returns>
public async Task<IEnumerable<Document>> GetEventsForDay(string dayKey)
{
    CheckCacheKeyExists(dayKey);
    var cachedItem = await Task.Run(async () =>
    {
        return await _cache.GetOrCreateAsync(dayKey, async entry =>
        {
            entry.SetOptions(_cacheEntryOptions);
            var events = await EventsForDay(dayKey).ConfigureAwait(false);
            return events;
        }).ConfigureAwait(false);
    }).ConfigureAwait(false);
    return cachedItem;
}

//
///这并不是死锁。这里的信用:https://stackoverflow.com/a/40569410
/// 
/// 
/// 
公共异步任务GetEventsForDay(字符串dayKey)
{
CheckCacheKeyExists(dayKey);
var cachedItem=wait Task.Run(异步()=>
{
return await\u cache.GetOrCreateAsync(dayKey,async entry=>
{
entry.SetOptions(_cacheEntryOptions);
var events=await events forday(dayKey).ConfigureAwait(false);
返回事件;
}).配置等待(错误);
}).配置等待(错误);
返回cachedItem;
}
我的问题是:

1.)为什么将异步lambda包装到
任务中。运行
会消除死锁

2.)如果
GetEventsForDay
之后的整个调用堆栈总是使用
ConfigureAwait(false)
作为任何
await
ed方法,那么为什么我的异步lambda首先会死锁?除了lambda本身被
wait
用自己的
ConfigureAwait(false)
等待之外,还有一个很好的问题

执行
Task.Run
时,传递给它的委托将在线程池中运行,但这里对您来说很重要,而不是使用
SynchronizationContext
。当您阻塞某些代码的结果时,这通常会导致死锁,而这些代码本身具有对该上下文的独占访问权

所以
Task.Run
“修复它”,除了它实际上没有,它只是掩盖了另一个问题,您正在阻止异步工作中的线程。这最终会导致加载时线程池不足,此时.NET无法添加更多线程来解除当前工作的阻塞

.ConfigureAwait(false)
使用
await
时意味着如果
任务
不完整,则不需要在当前
同步上下文
上运行以下代码,这也会导致前面提到的死锁情况

人们经常存在的一个疏忽是,当
任务
已经完成时,它实际上什么也不做,例如
任务.FromResult
,或者您有一些缓存的结果。在这种情况下,
SynchronizationContext
会进一步流入您的代码,从而增加后续代码在异步
任务仍然存在时阻塞该任务的可能性,从而导致死锁

回到您的示例,无论您在代码中放入多少
.ConfigureAwait(false)
,如果在
await
之前运行的代码对异步代码进行了一些阻塞,那么您仍然容易发生死锁。假设死锁的来源发生在
事件日
(不要这样做),这也可以为您修复死锁:

公共异步任务GetEventsForDay(字符串dayKey)
{
CheckCacheKeyExists(dayKey);
var cachedItem=await\u cache.GetOrCreateAsync(dayKey,async entry=>
{
wait Task.Delay(100).configurewait(false);//这也会停止死锁
entry.SetOptions(_cacheEntryOptions);
var事件=等待事件日(dayKey);
返回事件;
});
返回cachedItem;
}
这也会:

public async Task<IEnumerable<Document>> GetEventsForDay(string dayKey)
{
    CheckCacheKeyExists(dayKey);
    var cachedItem = await _cache.GetOrCreateAsync(dayKey, async entry =>
        {
            SynchronizationContext.SetSynchronizationContext(null); // this would stop it too
            entry.SetOptions(_cacheEntryOptions);
            var events = await EventsForDay(dayKey);
            return events;
        });
    return cachedItem;
}
公共异步任务GetEventsForDay(字符串dayKey)
{
CheckCacheKeyExists(dayKey);
var cachedItem=await\u cache.GetOrCreateAsync(dayKey,async entry=>
{
SynchronizationContext.SetSynchronizationContext(null);//这也会停止它
entry.SetOptions(_cacheEntryOptions);
var事件=等待事件日(dayKey);
返回事件;
});
返回cachedItem;
}

我可以想象
GetOrCreateAsync
调用表达式时没有
ConfigureAwait(false)
,因此尝试在当前(阻塞的)线程上恢复。ASP.NET核心没有同步上下文,因此
ConfigureAwait(false)
没有任何效果:如何声明
\u cacheEntryOptions
?它在哪里死锁?它是否会在调用
GetEventsForDay(…)
的位置死锁?或者在
wait\u cache.GetOrCreateAsync(…)
?这里还有另一个潜在问题。您的
\u缓存是什么类型的?如果
GetOrAddAsync()
可能会多次调用委托,那么您将启动多个任务
AsyncLazy
将帮助您避免这种情况。我不喜欢这两种解决方案,有没有人能提出另一种想法?但我确实喜欢这个理论,这和我们接受专业人士的教育是一样的。不管怎样,谢谢你。没问题,你不喜欢它们很好,它们是为了说明这是如何工作的,因此“不要这样做”。修复方法是从
事件日
中删除死锁场景,您需要共享死锁场景的代码,包括调用的任何第三方代码。@Stuart按照您的建议执行“错误”是什么?或者你是说找到死锁的根本原因比贴上绷带更好?正是这样,你正在努力解决这个问题并消除死锁,但如果你想让你的应用程序扩展,这可能会反过来影响你。@Stuart,你说,还有一个问题“当任务已经完成时,它实际上什么也不做,例如Task.FromResult”。为了澄清,“complete”任务可以在调用堆栈上的任何位置,对吗?因此,即使我有
configurewait(false)
无处不在
private async Task<IEnumerable<Document>> EventsForDay(string dayKey)
/// <summary>
/// This does not deadlock. Credit here: https://stackoverflow.com/a/40569410
/// </summary>
/// <param name="dayKey"></param>
/// <returns></returns>
public async Task<IEnumerable<Document>> GetEventsForDay(string dayKey)
{
    CheckCacheKeyExists(dayKey);
    var cachedItem = await Task.Run(async () =>
    {
        return await _cache.GetOrCreateAsync(dayKey, async entry =>
        {
            entry.SetOptions(_cacheEntryOptions);
            var events = await EventsForDay(dayKey).ConfigureAwait(false);
            return events;
        }).ConfigureAwait(false);
    }).ConfigureAwait(false);
    return cachedItem;
}

public async Task<IEnumerable<Document>> GetEventsForDay(string dayKey)
{
    CheckCacheKeyExists(dayKey);
    var cachedItem = await _cache.GetOrCreateAsync(dayKey, async entry =>
        {
            await Task.Delay(100).ConfigureAwait(false); // this would also stop the deadlock
            entry.SetOptions(_cacheEntryOptions);
            var events = await EventsForDay(dayKey);
            return events;
        });
    return cachedItem;
}
public async Task<IEnumerable<Document>> GetEventsForDay(string dayKey)
{
    CheckCacheKeyExists(dayKey);
    var cachedItem = await _cache.GetOrCreateAsync(dayKey, async entry =>
        {
            SynchronizationContext.SetSynchronizationContext(null); // this would stop it too
            entry.SetOptions(_cacheEntryOptions);
            var events = await EventsForDay(dayKey);
            return events;
        });
    return cachedItem;
}