BackgroundService 总是 运行 在新线程中吗

Will a BackgroundService always run in a new Thread

所以在 MSDN for ASP.Net Core 中,它向您展示了如何使用托管服务创建后台任务。甚至有一个特定的段落解释了如何创建后台队列。

现在我的问题是,ExecuteAsync 方法 运行 是否已经在其自己的线程中,还是我需要先调用 Task.Run

来自Asynchronous programming

For I/O-bound code, you await an operation which returns a Task or Task inside of an async method.

For CPU-bound code, you await an operation which is started on a background thread with the Task.Run method.

因此,如果您的 ExecuteAsync 方法是 I/O-bound(从名称上看它是 I/O-bound),那么您不需要打电话给 Task.Run

但是当方法是 CPU-bound(即您的代码正在执行计算)时,您应该调用 Task.Run 到 运行 在后台

will the ExecuteAsync method run in its own thread

假设 ExecuteAsync 是一个 async 方法 (public async Task ExecuteAsync)

Tl;这取决于医生

async 意味着这个线程可以 awaited。 await 将暂停主线程的当前执行,直到 async returns 的结果。这会将当前线程释放回线程池以供重新使用。然后当 async returns 从线程池中拉出一个新线程(可能取决于你实际调用它的方式)继续执行。这称为上下文切换。

如果这个方法不是真正的 async 那么什么也不会发生,它 运行 就好像它不是 async 方法一样。

如果此方法显式创建 Task(使用 Task.Run),则 async 线程将 await 这个 Task。所以任务使用一个新线程,async 方法将释放它的线程并在任务 returns 时获得一个新线程。这不是零和,因为上下文切换很昂贵。这就是为什么你应该只 async 因为你通常会失去效率而不是在 CPU 绑定过程中获得收益。

我建议你阅读 Stephen Cleary's excellent blogs on the subject

Will a BackgroundService always run in a new Thread?

BackgroundService 没有指定有关线程的任何内容。它唯一要求的是 return 是 Task 的重载,只要服务启动就保持活动状态。如果需要,您甚至可以 return 已完成 任务。

如果你 check the source code 你会发现根本没有假设:

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        return Task.CompletedTask;
    }

服务方法的线程行为取决于实现者,即您。如果 ExecuteAsync 在屈服之前阻塞,则整个服务阻塞。如果该方法永远不会产生,对 StartAsync 本身的调用将阻塞并导致整个应用程序出现问题。

如果 ExecuteAsync 在第一个 await 之前做了一些昂贵的事情,其他服务的启动也会延迟。

这意味着您可能需要使用 Task.Run 如果服务需要在第一次屈服之前做任何昂贵的事情,即第一次调用 await