从同步代码调用异步 I/O 方法
Calling asynchronous I/O method from sync code
我有这段代码(同步方式):
// ...
var processingTasks = Enumerable.Range(0, count)
.Select(x => Task.Run(async () =>
await CallApiAndUpdateSthAsync(accounts[x], cancellationToken)))
.ToList();
var processingTasksAggregate = Task.WhenAll(processingTasks);
Task.WaitAny(globalStatusMonitoringTask, processingTasksAggregate);
我对这部分特别感兴趣:
Task.Run(async () => await CallApiAndUpdateSthAsync(accounts[x], cancellationToken))
如果我删除 Task.Run
会有什么不同(从可能的死锁角度来看)吗? IE。更改为:
CallApiAndUpdateSthAsync(accounts[x], cancellationToken)
我想删除它,因为在线程池上排队任务(通过 Task.Run
)似乎是多余的,但我的同事建议我不要这样做,因为可能会出现死锁
整个方法运行作为类似于Hangfire的后台作业:
[编辑 1]:这是一个 .NET Framework(非核心)应用程序。
如果它是一个 asp.net core
应用程序(不仅仅是老派 asp.net
),则没有任务死锁问题。 SynchronizationContext
(没有 ConfigureAwait(false)
会导致死锁)在 asp.net core
中为 null。 There isn’t a SynchronizationContext in ASP.NET Core.
同样没有Task.Run
- CallApiAndUpdateSthAsync
将在你当前的线程上执行,直到第一个await
里面。使用 Task.Run
- 整个 CallApiAndUpdateSthAsync
将从您的主要执行中执行。您可以通过在 CallApiAndUpdateSthAsync
.
的开头添加 Task.Yield
来修复它
Would it make a difference (from a possible deadlock perspective) if I removed the Task.Run?
可能吧。 common deadlock(我的博客 link)分为两部分:
- 阻塞异步代码。您的代码使用
Task.WaitAny
. 执行此操作
- 一次只允许一个线程的上下文。
既然你的代码明显有(1),那么你可以断定它有(2)就会死锁。可能导致死锁的常见上下文包括 ASP.NET pre-Core 和 GUI 线程上下文。如果您的应用程序没有一次一个线程的上下文,那么就不会出现死锁。
I wanted to remove it, because queueing the task on a thread pool (via Task.Run) seemed redundant
不是真的。 “异步”并不意味着“运行 在不同的线程上”,因此两者并不多余。问题是删除 Task.Run
是否可行。可能会,也可能不会。如果此代码在单线程上下文中为 运行,则删除 Task.Run
将导致死锁。如果 CallApiAndUpdateSthAsync
可能 运行 同步(即,由于缓存命中),那么删除 Task.Run
将导致调用 运行 串行而不是并行,这可能也不理想。
我有这段代码(同步方式):
// ...
var processingTasks = Enumerable.Range(0, count)
.Select(x => Task.Run(async () =>
await CallApiAndUpdateSthAsync(accounts[x], cancellationToken)))
.ToList();
var processingTasksAggregate = Task.WhenAll(processingTasks);
Task.WaitAny(globalStatusMonitoringTask, processingTasksAggregate);
我对这部分特别感兴趣:
Task.Run(async () => await CallApiAndUpdateSthAsync(accounts[x], cancellationToken))
如果我删除 Task.Run
会有什么不同(从可能的死锁角度来看)吗? IE。更改为:
CallApiAndUpdateSthAsync(accounts[x], cancellationToken)
我想删除它,因为在线程池上排队任务(通过 Task.Run
)似乎是多余的,但我的同事建议我不要这样做,因为可能会出现死锁
整个方法运行作为类似于Hangfire的后台作业:
[编辑 1]:这是一个 .NET Framework(非核心)应用程序。
如果它是一个 asp.net core
应用程序(不仅仅是老派 asp.net
),则没有任务死锁问题。 SynchronizationContext
(没有 ConfigureAwait(false)
会导致死锁)在 asp.net core
中为 null。 There isn’t a SynchronizationContext in ASP.NET Core.
同样没有Task.Run
- CallApiAndUpdateSthAsync
将在你当前的线程上执行,直到第一个await
里面。使用 Task.Run
- 整个 CallApiAndUpdateSthAsync
将从您的主要执行中执行。您可以通过在 CallApiAndUpdateSthAsync
.
Task.Yield
来修复它
Would it make a difference (from a possible deadlock perspective) if I removed the Task.Run?
可能吧。 common deadlock(我的博客 link)分为两部分:
- 阻塞异步代码。您的代码使用
Task.WaitAny
. 执行此操作
- 一次只允许一个线程的上下文。
既然你的代码明显有(1),那么你可以断定它有(2)就会死锁。可能导致死锁的常见上下文包括 ASP.NET pre-Core 和 GUI 线程上下文。如果您的应用程序没有一次一个线程的上下文,那么就不会出现死锁。
I wanted to remove it, because queueing the task on a thread pool (via Task.Run) seemed redundant
不是真的。 “异步”并不意味着“运行 在不同的线程上”,因此两者并不多余。问题是删除 Task.Run
是否可行。可能会,也可能不会。如果此代码在单线程上下文中为 运行,则删除 Task.Run
将导致死锁。如果 CallApiAndUpdateSthAsync
可能 运行 同步(即,由于缓存命中),那么删除 Task.Run
将导致调用 运行 串行而不是并行,这可能也不理想。