运行 多个后台任务的最佳做法是什么
What is the best practice for running multiple background tasks
我有一个 Windows 服务 (.NET 4.5.2),它应该 运行 在后台执行多个任务,而我想使用 System.Threading.Tasks 以下哪个实现正在考虑最佳实践?还是我完全错了?
场景一:
protected override void OnStart(string[] args)
{
// Assume all tasks implemented the same way.
// I believe we shouldn't await the tasks in this scenario.
var token = this._cancellationTokenSource.Token;
this.RunTask1(token);
this.RunTask2(token);
this.RunTask3(token);
}
private async Task RunTask1(CancellationToken token)
{
var telebot = new Telebot("SOMETHING");
while( true )
{
// Some work...
// I/O dependent task.
var response = await telebot.GetUpdatesAsync(cancellationToken: token);
//
// Some other work
// maybe some database calls using EF async operators.
//
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
场景二:
protected override void OnStart(string[] args)
{
// Assume all tasks implemented the same way.
// I believe we shouldn't await the tasks in this scenario.
var token = this._cancellationTokenSource.Token;
this.RunTask1(token);
this.RunTask2(token);
this.RunTask3(token);
}
private void RunTask1(CancellationToken token)
{
Task.Factory.StartNew(async () =>
{
var telebot = new Telebot("SOMETHING");
while( true )
{
// Some work...
// I/O dependent task.
var response = await telebot.GetUpdatesAsync(cancellationToken: token);
//
// Some other work
// may be some database calls using EF async operators.
//
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}, token);
}
我无法解释哪个是最好的,但事情是这样的
在 1. 场景代码中,直到 await 关键字被父线程执行,即应用程序的主线程。所以一旦执行等待任务执行完成由保存的上下文处理的事情,即主线程上下文。
在 2. 场景代码中,它在任务工厂创建的线程上启动 运行ning。这里一旦执行等待任务执行完成由父级处理的事情,即任务工厂创建的线程。
所以在第一种情况下,如果您想 post 主线程的某些内容主要是 UI 应用程序,那很好。如果你想在后台 运行 事情并且不需要父上下文,即主线程或 UI 线程,第二种情况是好的。
async
方法同步 运行 秒,直到第一个 await
。之后它将 运行 在 ThreadPool 线程上(除非有 SynchronizationContext
)。
因此,不鼓励使用 Task.Factory.StartNew
或 Task.Run
,因为它试图并行化大部分已经并行的内容。
但是,如果您有大量同步部分,则使用 Task.Run
(优于 Task.Factory.StartNew
)对其进行并行化会很有用,但您应该在调用方法和不在方法本身。
因此,"Scenario 1" 优于 "Scenario 2"。
我认为您不应该开火并忘记这些操作。您应该存储任务,等待它们完成并观察其中的任何异常,例如:
protected override void OnStart()
{
var token = _cancellationTokenSource.Token;
_tasks.Add(RunTask1(token));
_tasks.Add(RunTask2(token));
_tasks.Add(Task.Run(() => RunTask3(token))); // assuming RunTask3 has a long synchronous part
}
List<Task> _tasks;
protected override void OnStop()
{
_cancellationTokenSource.Cancel();
Task.WhenAll(_tasks).Wait();
}
我有一个 Windows 服务 (.NET 4.5.2),它应该 运行 在后台执行多个任务,而我想使用 System.Threading.Tasks 以下哪个实现正在考虑最佳实践?还是我完全错了?
场景一:
protected override void OnStart(string[] args)
{
// Assume all tasks implemented the same way.
// I believe we shouldn't await the tasks in this scenario.
var token = this._cancellationTokenSource.Token;
this.RunTask1(token);
this.RunTask2(token);
this.RunTask3(token);
}
private async Task RunTask1(CancellationToken token)
{
var telebot = new Telebot("SOMETHING");
while( true )
{
// Some work...
// I/O dependent task.
var response = await telebot.GetUpdatesAsync(cancellationToken: token);
//
// Some other work
// maybe some database calls using EF async operators.
//
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
场景二:
protected override void OnStart(string[] args)
{
// Assume all tasks implemented the same way.
// I believe we shouldn't await the tasks in this scenario.
var token = this._cancellationTokenSource.Token;
this.RunTask1(token);
this.RunTask2(token);
this.RunTask3(token);
}
private void RunTask1(CancellationToken token)
{
Task.Factory.StartNew(async () =>
{
var telebot = new Telebot("SOMETHING");
while( true )
{
// Some work...
// I/O dependent task.
var response = await telebot.GetUpdatesAsync(cancellationToken: token);
//
// Some other work
// may be some database calls using EF async operators.
//
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}, token);
}
我无法解释哪个是最好的,但事情是这样的
在 1. 场景代码中,直到 await 关键字被父线程执行,即应用程序的主线程。所以一旦执行等待任务执行完成由保存的上下文处理的事情,即主线程上下文。
在 2. 场景代码中,它在任务工厂创建的线程上启动 运行ning。这里一旦执行等待任务执行完成由父级处理的事情,即任务工厂创建的线程。
所以在第一种情况下,如果您想 post 主线程的某些内容主要是 UI 应用程序,那很好。如果你想在后台 运行 事情并且不需要父上下文,即主线程或 UI 线程,第二种情况是好的。
async
方法同步 运行 秒,直到第一个 await
。之后它将 运行 在 ThreadPool 线程上(除非有 SynchronizationContext
)。
因此,不鼓励使用 Task.Factory.StartNew
或 Task.Run
,因为它试图并行化大部分已经并行的内容。
但是,如果您有大量同步部分,则使用 Task.Run
(优于 Task.Factory.StartNew
)对其进行并行化会很有用,但您应该在调用方法和不在方法本身。
因此,"Scenario 1" 优于 "Scenario 2"。
我认为您不应该开火并忘记这些操作。您应该存储任务,等待它们完成并观察其中的任何异常,例如:
protected override void OnStart()
{
var token = _cancellationTokenSource.Token;
_tasks.Add(RunTask1(token));
_tasks.Add(RunTask2(token));
_tasks.Add(Task.Run(() => RunTask3(token))); // assuming RunTask3 has a long synchronous part
}
List<Task> _tasks;
protected override void OnStop()
{
_cancellationTokenSource.Cancel();
Task.WhenAll(_tasks).Wait();
}