如何让长进程在后台运行而不中断UI

问题描述 投票:0回答:1

我正在尝试创建一个函数来创建 ItemObj,然后导入一些与其相关的请求。问题是它使用外部 API 带来大量数据,这使得这一步变慢。

我们希望将导入保留在后台,而 UI 仍然可以使用。

public async Task<ItemObjDto> CreateItemObjAsync(ItemObjViewModel itemObjViewModel)
{
    var itemObj = CreateItemObj(itemObjViewModel);
    itemObjRepository.AddAsync(itemObj);
    await itemObjRepository.SaveAsync();

    // This import should happen in the background
    _requestAppService.ImportRequestsAsync(itemObj.Id);

    return _mapper.Map<ItemObjDto>(itemObj);
}

我看过很多帖子都提出了这样的建议:

await Task.Run(() =>
{
    _requestAppService.ImportRequestsAsync(itemObj.Id);
});

但是,如果我将它与等待一起使用,它不会变得异步,如果我删除它,它就会出现错误,因为它无法正确保存。由于某种原因,我什至无法正确调试它,因为它不会在 try/catch 中停止,无论是在它周围,还是在 _requestAppService 内部。

我还在这篇文章中看到了一种更复杂的方法来解决这个问题:https://stackoverflow.com/a/63429262,但我在尝试让它通过导入请求调用我的 appService 时遇到了一些麻烦。 .

我想知道这两种解决方案是否是我正在寻找的,以及您是否可以告诉我我做错了什么

c# asp.net-core asynchronous background-process
1个回答
0
投票

如果我将它与等待一起使用,它不会变得异步,并且如果我 删除它,它会变得有问题,因为它无法正确保存。而对于 出于某种原因,我什至无法正确调试它,因为它不会停止 在 try/catch 中,既不在它周围,也不在 _requestAppService 内部。 我想知道这两种解决方案是否是我正在寻找的 如果你能告诉我我做错了什么

根据您的场景和描述以及共享代码片段,我发现了一些不一致的代码,这些代码有缺点,可能导致异常输出或性能缺陷。

当你的任务是

Task.Run(())
时,你使用了await,这是不推荐的。因为,在 Task.Run 块中使用 await 可能效率低下,甚至会损害应用程序的性能和响应能力。

最重要的是,

Task.Run's
的目的是将非 UI 工作从线程池卸载到后台线程上。因此,在 Task.Run 中添加 await 本质上会阻塞后台线程,同时等待另一个操作完成。

此外,当您在 Task.Run 中等待时,您会在后台线程和调用线程之间反复切换。这种上下文切换会增加开销,并对应用程序的整体性能产生负面影响。

因此,您可以修改代码如下:

public async Task<ItemObjDto> CreateItemObjAsync(ItemObjViewModel itemObjViewModel)
{
    var itemObj = CreateItemObj(itemObjViewModel);
    itemObjRepository.AddAsync(itemObj);
    await itemObjRepository.SaveAsync();

    
    _ = Task.Run(async () =>
    {
        await _requestAppService.ImportRequestsAsync(itemObj.Id).ConfigureAwait(false);
    });

    return _mapper.Map<ItemObjDto>(itemObj);
}

注意: 请记住,这不是您遇到的问题的确切解决方案。但它肯定会大幅提高您的应用程序性能。另外,你可以使用

ConfigureAwait(false)
来避免死锁。请参阅此官方文档了解更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.