【发布时间】:2020-09-03 05:51:12
【问题描述】:
环境: Windows Server 2012、.net 4.5、Visual Studio 2013。
注意:不是 UI 应用程序(因此与著名的 async/await/synchronizationcontext 问题无关)(参考:http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-library-methods-should-consider-using-Task-ConfigureAwait-false-)
编辑
这是一个导致死锁的错字。我在下面粘贴了导致死锁的示例 sn-p(伪)。基本上,我不是在“子任务”上编写 whenall,而是在“外部任务”上编写的:(。看起来我不应该在看电视时编写“异步”代码:)。
我保留了原始代码 sn-ps,因为它确实回答了我的第一个问题(我之前的两个代码 sn-ps 中与 async/await 和 unwrap 的区别),因此它为 Kirill 的响应提供了上下文。僵局让我分心看实际问题:)。
static void Main(string[] args)
{
Task t = IndefinitelyBlockingTask();
t.Wait();
}
static Task IndefinitelyBlockingTask()
{
List<Task> tasks = new List<Task>();
Task task = FooAsync();
tasks.Add(task);
Task<Task> continuationTask = task.ContinueWith(t =>
{
Task.Delay(10000);
List<Task> childtasks = new List<Task>();
////get child tasks
//now INSTEAD OF ADDING CHILD TASKS, i added outer method TASKS. Typo :(:)!
Task wa = Task.WhenAll(tasks/*TYPO*/);
return wa;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
tasks.Add(continuationTask);
Task unwrappedTask = continuationTask.Unwrap();
tasks.Add(unwrappedTask);
Task whenall = Task.WhenAll(tasks.ToArray());
return whenall;
}
当“解包”延续任务添加到聚合/任务链时无限期等待的代码片段我已粘贴在下面 伪(在我的实际应用程序中的模式/成语块 - 不是示例) 代码 sn-p (#1),当 'unwrapped' 任务被添加到列表时,它会无限期地等待。而 VS Debugger 'Debugger + windows + threads' 窗口显示线程只是阻塞在 ManualResetEventSlim.Wait。
与 async/await 配合使用的代码片段,并删除未包装的任务 然后我删除(在调试时随机)这个 unwrap 语句并在 lambda 中使用 async/await(请参见下文)。令人惊讶的是它有效。但我不知道为什么 :(?.
问题
-
在下面的代码 sn-ps 中使用 unwrap 和 async/await 的目的不是相同吗?我最初只是更喜欢 sn-p #1,因为我只是想避免生成过多的代码,因为调试器不是那么友好(特别是在异常通过链式任务传播的错误场景中 - 异常中的调用堆栈显示 movenext 而不是我的实际代码)。如果是,那是不是 TPL 的 bug?
-
我错过了什么?如果它们相同,则首选哪种方法?
关于调试器 + 任务窗口的注意事项“调试器 + 任务”窗口不显示任何详细信息(请注意,它在我的环境中无法正常工作(至少我的理解),因为它从不显示未计划的任务和依次显示活动任务)
代码 sn-p 无限期等待 ManualResetEventSlim.Wait
static Task IndefinitelyBlockingTask()
{
List<Task> tasks = new List<Task>();
Task task = FooAsync();
tasks.Add(task);
Task<Task> continuationTask = task.ContinueWith(t =>
{
List<Task> childTasks = new List<Task>();
for (int i = 1; i <= 5; i++)
{
var ct = FooAsync();
childTasks.Add(ct);
}
Task wa = Task.WhenAll(childTasks.ToArray());
return wa;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
tasks.Add(continuationTask);
Task unwrappedTask = continuationTask.Unwrap();
//commenting below code and using async/await in lambda works (please see below code snippet)
tasks.Add(unwrappedTask);
Task whenall = Task.WhenAll(tasks.ToArray());
return whenall;
}
代码 sn-p 与 lambda 中的 async/await 配合使用,而不是展开
static Task TaskWhichWorks()
{
List<Task> tasks = new List<Task>();
Task task = FooAsync();
tasks.Add(task);
Task<Task> continuationTask = task.ContinueWith(async t =>
{
List<Task> childTasks = new List<Task>();
for (int i = 1; i <= 5; i++)
{
var ct = FooAsync();
childTasks.Add(ct);
}
Task wa = Task.WhenAll(childTasks.ToArray());
await wa.ConfigureAwait(continueOnCapturedContext: false);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
tasks.Add(continuationTask);
Task whenall = Task.WhenAll(tasks.ToArray());
return whenall;
}
显示阻塞代码的调用栈
mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Threading.Tasks.Task.Wait() Unknown
【问题讨论】:
-
您是否从 ui 应用程序运行并且 fooasync 是否包含等待?
-
@Linky:它不是“UI”应用程序。还要注意,由于“等待”,该块没有发生 - 所以我认为它与 UI/同步上下文的等待问题无关(参考:channel9.msdn.com/Series/Three-Essential-Tips-for-Async/…)
-
所以您在没有任何特殊同步上下文的服务/控制台应用程序中运行?
-
@Linky:是的,还要注意代码在使用“等待”时有效(所以我猜它与此无关)。如果我在等待的任务链中包含“未包装的延续任务”,它会阻塞。如果我删除了未包装的任务并简单地使用 await 一切都很好。
-
这真的很奇怪。我的设置和你差不多。在控制台中使用 'await Task.Delay' 的虚拟实现运行 FooAsync 并在其上运行 'Wait()' 或 'await' 可以按预期工作,在 UI 应用程序的 UI 死锁中运行相同。很想知道答案还有什么问题。
标签: c# .net multithreading task-parallel-library async-await