运行多个后台任务的最佳实践是什么?

时间:2015-07-13 09:00:52

标签: c# async-await task-parallel-library task

我有一个Windows服务(.NET 4.5.2),它应该在后台运行多个任务,而我想使用System.Threading.Tasks您正在考虑哪个以下实现最佳实践?或者我完全错了?

情景1:

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);
    }
}

情景2:

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);
}

2 个答案:

答案 0 :(得分:2)

我无法解释哪个是最好的,但这里的工作方式

在1.场景代码中的

直到await关键字由父线程执行,即应用程序的主线程。因此,一旦执行等待任务执行完成由上下文处理的事物,即保存即主线程上下文。

在2.场景代码中,它开始在由Task Factory创建的线程上运行。这里一旦执行等待任务执行完成由父项处理的事情,即由任务工厂创建的线程。

所以在第一个场景中,如果你想将主要线程发布到应用程序的UI主要是好的。如果你想在后台运行东西并且不需要父上下文,即主线程或UI线程,那么第二种情况是好的。

答案 1 :(得分:0)

async方法同步运行,直到第一个await。之后它将在ThreadPool线程上运行(除非有SynchronizationContext)。

因此,不鼓励使用Task.Factory.StartNewTask.Run,因为它正在尝试并行化大部分已经并行的内容。

但是,如果你有一个实质性的同步部分,那么使用Task.Run(比Task.Factory.StartNew更可取)并行化它是有用的,但你应该在调用方法时这样做而不是方法本身。

因此,“场景1”优于“场景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();
}