gpt4 book ai didi

c# - 创建、不等待和确保任务完成的正确方法

转载 作者:行者123 更新时间:2023-12-05 01:04:35 35 4
gpt4 key购买 nike

前言:我对 C# 中任务的底层实现不太了解,只了解它们的用法。为我在下面屠宰的任何东西道歉:

对于“我怎样才能开始一项任务但不等待它?”这个问题,我找不到一个好的答案。在 C# 中。更具体地说,即使任务的上下文已完成/销毁,我如何保证任务完成?

_ = someFunctionAsync(); 对于启动和忘记任务来说是令人满意的,但是如果父级是 transient 的呢?如果任务无法在父任务之前完成怎么办?这在 Controller 方法中经常发生,以 _ = someFunctionAsync(); 方式编写的任务可能会被取消。

示例代码:

        [HttpGet]
public IActionResult Get()
{
_ = DoSomethingAsync();
return StatusCode(204);
}

为了解决这种取消,我创建了一个(相当愚蠢,imo)静态类来保留任务,以便他们有时间完成,但它不起作用,因为当父 Controller 被取消时任务被取消销毁:

    public static class IncompleteTaskManager
{
private static ConcurrentBag<Task> _incompleteTasks = new();

private static event EventHandler<Task>? _onTaskCompleted;

public static void AddTask(Task t)
{
_onTaskCompleted += (sender, task) =>
{
_incompleteTasks = new ConcurrentBag<Task>(_incompleteTasks.Where(task => task != t));
};

_incompleteTasks.Add(CreateTaskWithRemovalEvent(t));
}

private static async Task CreateTaskWithRemovalEvent(Task t)
{
await t;
_onTaskCompleted?.Invoke(null, t);
}
}

另外,这看起来很复杂,感觉像是一个简单问题的糟糕解决方案。那么,我该怎么处理呢?什么是开始一项任务,忘记它,但保证它运行完成的正确方法?

编辑 1,以防有人建议:我读过帖子建议 _ = Task.Run(async () => await someFunctionAsync()); 可能满足我的需求,但这也不是这样。虽然另一个线程运行该方法,但它的上下文也丢失了,并且该任务被取消,从而取消了子任务。

编辑 2:我意识到 Controller 示例不一定是最好的,因为我可以简单地编写不同的代码以立即响应,然后在处理 Controller 之前等待方法完成:

        [HttpGet]
public async Task Get()
{
base.Response.StatusCode = 204;
await base.Response.CompleteAsync(); //Returns 204 to caller here.
await DoSomethingAsync();
}

最佳答案

这里有很多东西要解压。我可能会错过一些细节,但让我分享一些应该奠定良好基础的东西。

从根本上说,您所问的是如何在 ASP.NET 中创建后台任务。在 .NET 4.x 中,有一个 QueueBackgroundWorkItem为此目的创建的方法:它为您的任务提供了一个新的取消 token 来使用,而不是 Controller 操作提供的取消 token ,并将您切换到您提供的操作的不同上下文。

在 asp.net 核心中,有更强大(但更复杂)的 IHostedService 实现,包括 BackgroundService,但没有比 QueueBackgroundWorkItem 更简单的了。但是,the docs include an example展示了如何使用 BackgroundService 来编写自己的实现。如果您使用他们的代码,您应该能够将 IBackgroundTaskQueue 注入(inject)您的 Controller 并调用 QueueBackgroundWorkItemAsync 以将后台任务排入队列。

这两种方法都需要有一些东西等待开始的任务。您永远无法真正“保证”完成任何给定任务,但它们至少可以更优雅地处理常见用例。例如,它们让您的托管环境(例如 IIS)知道某些东西仍在运行,因此它不会因为没有请求进入而自动关闭。如果托管环境被指示关闭,它可以发出信号这个事实通过取消 token ,您可以希望快速让您的任务进入安全状态以关闭而不是被随意中止。

它们还处理后台任务中未捕获异常的问题:异常被捕获并记录下来,而不是被静默吃掉或完全杀死应用程序。

这些都不能帮助您维护有关当前请求或用户等内容的上下文。这是明智的,因为重点是允许操作超出任何给定请求的范围。因此,您需要编写在这些操作中调用的任何代码,以不依赖 HttpContext/IHttpContextAccessor 或类似的任何有状态的东西。相反,在将后台任务排入队列之前从上下文中收集您需要的信息,并将该信息作为变量和参数传递给下游代码。无论如何,这通常是一种很好的做法,因为 HTTP 上下文是应该保留在 Controller 级代码中的职责,而您的大多数业务逻辑应该考虑业务级模型。通常最好尽可能避免依赖状态,以创建更可靠、可测试等的软件。

对于其他类型的应用程序,您还需要采用其他方法。通常最好对 [framework] background tasks 进行互联网搜索,其中 [framework] 是您正在使用的框架(例如 WPF)。不同的框架会有不同的限制。例如,如果您编写了一个控制台应用程序,该应用程序希望在没有命令行参数之外的任何交互的情况下运行,则从您的 Main 函数返回的 Task 可能需要等待您在其中启动的所有任务。另一方面,WPF 应用程序可能会在调用按钮单击等事件时启动多个后台任务,但有一些技巧可确保您在后台线程上执行 CPU 密集型工作,同时仅在 UI 线程上与 UI 元素交互.

关于c# - 创建、不等待和确保任务完成的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71899322/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com