繁体   English   中英

C# ASP.NET Core 一劳永逸

[英]C# ASP.NET Core Fire and forget

我有一个同步控制器方法

    public IActionResult Action(int num)
    {
        //operations-1

        Task.Run(() => DoWork(num));

        //operations-2
        int a = num + 123;
        return Ok(a);
    }

和 DoWork 方法

    private bool DoWork(int num)
    {
        //operations

        return true;
    }

我想要做的是在从 Postman 调用该端点时在后台运行 DoWork 方法,但我想在 Postman 中获得结果,然后调试 DoWork 方法(从 DoWork 方法中的断点) - 可能吗?

在那一刻,控制器动作和 DoWork() 正在同时执行,但是当我到达

return Ok(a);

应用程序等待 DoWork 而不是返回值。 我也试过

Task.Factory.StartNew(() => DoWork());
ThreadPool.QueueUserWorkItem(o => DoWork());

但结果是一样的。

我希望 DoWork 方法返回值,但控制器操作方法不需要该值 - 它将在不同的地方使用,而不是与之连接。

任务是确保您没有阻塞任何上下文的高级线程

您要么想使用 ASP.NET Core 2.0 中的RabbitMQIHostedService 之类的东西来执行触发并忘记在请求完成后启动的任务。

如果在项目中使用 Db,则可以使用 Hangfire 后台进程管理器,非常好用。 https://www.hangfire.io/

您可以像BackgroundJob.Enqueue(() => DoWork(num));一样轻松使用它

坦率地说,这看起来像是XY问题 在控制器中执行火灾并忘记呼叫似乎没有多大意义。 如果您知道您的操作将花费一些时间,那么使用队列和出队Webjob可能是一个更好的主意。

话虽如此,您想要实现的目标是可能的:您的DoWork方法只需要async

public IActionResult Action(int num)
{
    //operations-1
    Task.Run(() => DoWork(num)); // async call not awaited

    //operations-2
    int a = num + 123;
    return Ok(a);
}

public async Task<bool> DoWork(int num)
{
    await Task.Delay(10000);
    var i = 9; // breakpoint will hit after 10s
    return true;
}

有时使用后台队列是矫枉过正的。 当您需要访问数据库上下文时,有许多站点提供了一种方法。 Task.Run 在控制器中的问题在于,生成的任务无法访问与控制器使用的上下文相同的上下文,因为它可能(可能会)在该任务访问它之前被释放。 您可以通过确保子任务仅引用它知道将保持活动状态的依赖项来解决此问题,无论是通过使用单例服务还是更好地用于数据库上下文,使用IServiceScopeFactory

关键是创建一个单独的依赖项来处理您的数据库上下文或存储库。 这可以像往常一样注入到您的控制器中。

    public void Execute(Func<IRepository, Task> databaseWork)
    {
        // Fire off the task, but don't await the result
        Task.Run(async () =>
        {
            // Exceptions must be caught
            try
            {
                using var scope = _serviceScopeFactory.CreateScope();
                var repository = scope.ServiceProvider.GetRequiredService<IRepository>();
                await databaseWork(repository);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        });
    }

然后从您的控制器调用它,例如

        // Delegate the slow task another task on the threadpool
        _fireForgetRepositoryHandler.Execute(async repository =>
        {
            // Will receive its own scoped repository on the executing task
            await repository.DoLOngRunningTaskAsync();;
        });

注意:这建立在 Adem Catamak 的回答之上。

可以使用 Hangfire,但不需要实际的数据库,因为它可以使用内存存储

services.AddHangfire(opt => opt.UseMemoryStorage());
JobStorage.Current = new MemoryStorage();

虽然它有一些开销,但 Hangfire 允许管理这些作业,而不是让东西运行异步并且需要自定义代码来处理简单的事情,例如运行时、未处理的异常、用于 DI 支持的自定义代码。

学分:抄本

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM