在C#中使用ContinueWith链接异步任务

时间:2018-05-24 13:11:28

标签: c# async-await task

尽管asyncawait已经出现了一段时间,并且由于长时间使用C#dev,我仍然很难理解它们的工作原理以及何时使用它们。所以我正在写一些测试代码!

我正在尝试 asynchronoulsy 主题调用任务A 然后调用任务A 任务A完成后,然后调用任务C

在伪代码中,这将是:

RunAsync(TaskA())
    .Then(TaskB())
        .Then(TaskC());

我写了以下示例,但是没有按照我的预期行事。相反或运行A然后运行B然后运行A,它运行A,然后B C运行在并行

C#片段如下(详情如下):

Task
  .Run(async () => await LongTaskAsync("A"))
  .ContinueWith(async (taskA) => await LongTaskAsync("B"))
  .ContinueWith(async (taskB) => await LongTaskAsync("C"));

我打印出了线程ID,我有:

  • Id 1 for main thread
  • ID 2用于任务A
  • ID 4代表任务B和C
  • 否Id 3(或我忽略的某个地方)

以下是代码:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncTests
{
    class Program
    {
        private static DateTime _start;
        static void Main(string[] args)
        {
            _start = DateTime.Now;
            Log("======= Main thread starts ======");
            Log($"Main thread ID : {Thread.CurrentThread.ManagedThreadId}");

            // Start a stack of aynchronous calls
            Task
                .Run(async () => await LongTaskAsync("A"))
                .ContinueWith(async (taskA) => await LongTaskAsync("B"))
                .ContinueWith(async (taskB) => await LongTaskAsync("C"));

            Log("====== Main thread returns ======");
            Console.ReadKey();
        }  

        static async Task LongTaskAsync(string name)
        {
            Log($"Long async task {name} starts");
            Log($"{name} thread ID : {Thread.CurrentThread.ManagedThreadId}");
            for(var i = 1 ; i <= 5 ; i++)
            {
                Log($"Task {name} says {i}");
                await Task.Delay(1000);
            }            
            Log($"Long async task {name} returns");
        }

        static void Log(string text)
        {
            var elpased = (int)(DateTime.Now - _start).TotalMilliseconds;
            Console.WriteLine($"[+{elpased.ToString().PadLeft(4,'0')}] {text}");
        }
    }
}

控制台输出:

[+0003] ======= Main thread starts ======
[+0009] Main thread ID : 1
[+0016] ====== Main thread returns ======
[+0022] Long async task A starts
[+0024] A thread ID : 3
[+0024] Task A says 1
[+1028] Task A says 2
[+2029] Task A says 3
[+3030] Task A says 4
[+4031] Task A says 5
[+5032] Long async task A returns
[+5034] Long async task B starts
[+5034] B thread ID : 4
[+5034] Task B says 1
[+5035] Long async task C starts
[+5036] C thread ID : 4
[+5036] Task C says 1
[+6036] Task C says 2
[+6036] Task B says 2
[+7037] Task C says 3
[+7037] Task B says 3
[+8038] Task B says 4
[+8038] Task C says 4
[+9039] Task C says 5
[+9039] Task B says 5
[+10040] Long async task C returns
[+10040] Long async task B returns

1 个答案:

答案 0 :(得分:2)

如果您希望按顺序拨打电话,只需await他们:

Task.Run(async () =>
{
    await LongTaskAsync("A");
    await LongTaskAsync("B");
    await LongTaskAsync("C");
});

示例输出:

[+0000] ======= Main thread starts ======
[+0001] Main thread ID : 1
[+0015] ====== Main thread returns ======
[+0020] Long async task A starts
[+0020] A thread ID : 3
[+0020] Task A says 1
[+1021] Task A says 2
[+2021] Task A says 3
[+3022] Task A says 4
[+4022] Task A says 5
[+5024] Long async task A returns
[+5024] Long async task B starts
[+5024] B thread ID : 4
[+5024] Task B says 1
[+6025] Task B says 2
[+7026] Task B says 3
[+8026] Task B says 4
[+9027] Task B says 5
[+10028] Long async task B returns
[+10028] Long async task C starts
[+10028] C thread ID : 3
[+10028] Task C says 1
[+11029] Task C says 2
[+12029] Task C says 3
[+13030] Task C says 4
[+14032] Task C says 5
[+15032] Long async task C returns

但是,如果任何任务失败(由于await),上述代码将失败。如果您希望这些始终运行,您将需要一个更丑陋的方法:

Task.Run(() =>
{
    LongTaskAsync("A").ContinueWith((taskA) =>
    {
        LongTaskAsync("B").ContinueWith((taskB) => LongTaskAsync("C"));
    });
});

这与您的实际代码之间的区别在于您为继续工作分配的任务。