【问题标题】:Tasks executing on own thread在自己的线程上执行的任务
【发布时间】:2018-02-12 18:39:00
【问题描述】:

我有一个 Action 可以发送到三个 Cosmos DB 存储库以检索信息。动作代码如下:

var task1 = _itemRepository.RetrieveByIdAsync(id);
var task2 = _partsRepository.RetrieveForItemAsync(id);
var task3 = _purchasesRepository.RetrieveForItemAsync(id, currentUserId);

var userPurchased = await _userPurchaseRepository.HasUserPurchased(Session.UserId) // this is awaited because it is a SQL task and can only execute one at a time

await Task.WhenAll(task1, task2, task3);

我在调试时注意到的是任务似乎在不同的线程上运行。 System.Threading.Thread.CurrentThread.ManagedThreadId 与任务运行前、每个任务以及等待任务后不同。

进入前的线程 ID - 5
项目存储库线程 ID - 61
部件存储库线程 ID - 5
购买存储库线程 ID - 60
WaitAll - 6 后的线程 ID

存储库方法最终都会调用一个通用的 QueryAsync 方法,如下所示:

public async Task<IEnumerable<TDomainObject>> QueryAsync(Expression<Func<TDomainObject, bool>> predicate, FeedOptions options)
{
    var query = Client.CreateDocumentQuery<TDomainObject>(Collection.DocumentsLink, options).Where(predicate).AsDocumentQuery();

    List<TDomainObject> results = new List<TDomainObject>();
    while (query.HasMoreResults)
    {
        results.AddRange(await query.ExecuteNextAsync<TDomainObject>());
    }

    return results;
}

我的理解是,所有异步/等待处理都应该使用同一个请求线程,我担心我的代码可能无法达到应有的性能。

我没有将线程 ID 用于任何事情,我只是想确保我没有创建不必要的线程。

编辑:最初让我对此进行调查的是,我发现 QueryAsync 方法中的调用堆栈,当断点结果时,调用堆栈开始于存储库而不是在控制器中。

【问题讨论】:

  • ASP.NET 调度程序没有线程关联。继续处理完成通知的任何线程池线程是一件好事,它可以节省上下文切换。
  • 只有当你使用线程 ID 做某事时,这才是一个问题。首先,Wich 是个坏主意。如果您需要识别任务,则应通过在启动期间为其提供/检索的变量来完成。像一个简单的运行计数器(不要忘记检索/更新期间的竞争条件保护)。
  • ASP.NET Core 使用/管理线程池。当您启动一个请求时,它会在一个线程上运行。当你等待一个异步 I/O 任务时,原来的线程会返回到线程池中,并且可以用于其他请求,直到操作完成,然后它会从线程池中选择另一个空闲/可用的线程继续执行。它可能与原始线程相同,也可能不同(因为原始线程可能已被另一个请求或延续使用)。除非您调用 Task.RunTaskFactory.StartNew(在 99% 的情况下在 Web 应用程序中不好),否则不会产生新线程
  • @Tseng 不建议显式使用ConfigureAwait(false),否则继续会请求同一个线程并可能导致延迟
  • @MrinalKamboj:是的,这是编写可重用库的推荐方法。在 ASP.NET Core 中不再需要,因为不再有 AspNetSynchronizationContext。事实上,ASP.NET Core 放弃了在 ASP.NET Core 代码中使用.ConfigureAwait(false)。但在通用库(可用于桌面、UI、WPF 或移动应用程序)中,仍然建议在每个等待的任务上调用.ConigureAwait(false)。但是在纯 ASP.NET Core 库中(依赖于 Microsoft.AspNetCore.* 库的库不再需要了

标签: c# multithreading asp.net-core async-await


【解决方案1】:

Task Parallel Library 和 async/await 功能的全部目的是抽象出底层线程逻辑。除非您在一个项目中使用具有在 UI 线程上运行的 同步上下文 的框架(即:WinForms、WPF、Xamarin),否则您应该让框架确定给定的 @ 987654321@ 应该在当前线程或其他线程上运行。

【讨论】:

    猜你喜欢
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多