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

C# 在C中使用ContinueWith链接异步任务#,c#,async-await,task,C#,Async Await,Task,尽管async和await已经推出一段时间了,而且作为一名长期的C#dev,我仍然很难真正理解它们是如何工作的以及何时使用它们。所以我正在写一些测试代码 我正在尝试从主线程异步调用任务a,然后调用任务B当任务a完成时,然后调用任务C 在伪代码中,这类似于: RunAsync(TaskA()) .Then(TaskB()) .Then(TaskC()); 我已经编写了下面的示例,但在中的行为与我预期的不同。相反,或者先运行A,然后运行B,然后运行C,它运行A,然后运行B和

尽管
async
await
已经推出一段时间了,而且作为一名长期的C#dev,我仍然很难真正理解它们是如何工作的以及何时使用它们。所以我正在写一些测试代码

我正在尝试从主线程异步调用任务a然后调用任务B当任务a完成时,然后调用任务C

在伪代码中,这类似于:

RunAsync(TaskA())
    .Then(TaskB())
        .Then(TaskC());
我已经编写了下面的示例,但在中的行为与我预期的不同。相反,或者先运行A,然后运行B,然后运行C,它运行A,然后运行BC,以并行方式运行

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

我打印了线程ID,我有:

  • 主线程的Id 1
  • 任务A的Id 2
  • 任务B和C的Id 4
  • 没有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}");
        }
    }
}

如果您希望按顺序进行呼叫,只需等待它们:

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
但是,如果任何任务失败(由于
wait
),上述代码将失败。如果你想让它们一直运行,你需要一种更丑陋的方法:

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

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

为什么您要使所有lambda
异步
,而它们什么都不做,但却反对在一个可以实际使用它的方法中使用
异步
,我对C#或任何语言的异步编程都不太熟悉。因此,对这些关键词的误用是极有可能的。关于复制,我不认为这个问题是一个重复,因为我的细节是链几个连续,而其他涉及一个单一的调用它。基本上,我的问题是:“在JS/TS中,是否应该考虑<代码/继续”作为“/-代码”的“C”等价物。事实上,你需要应用这个解决方案三次,而不仅仅是一次,这并不是一个根本不同的问题。如果你正在做需要修复三次的事情,那么应用解决方案三次。这正是我所期望的,而且比我的版本可读性好得多。当一个
任务
抛出异常时,其他任务将不会执行,而
ContinueWith
则不是这样。此外,在
任务中包装等待。Run
几乎没有实际意义use@MrinalKambojOP没有提到他们希望在任务失败时处理案例。是的,它确实具有在线程池线程中运行任务的实际用途。最好使整个链包括主异步,而不是将其包装在
任务中。运行
并使用一个池线程执行异步operation@kall2sollies我之前没有意识到你的问题被标记为重复。现在,不管怎样,当前的答案包含了一个重要的区别,即ContinueWith与等待呼叫集的不同程度。您当然可以针对特定的语言版本编译器。请考虑将程序入口点设为异步,这对于ASP.NETMVC/API控制器、控制台主控是可能的,这是启动非阻塞异步调用的理想方式。您甚至可以计划使用
Task.WhenAll
WhenAny
一起执行多个异步调用并等待代表性任务
[+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
Task.Run(() =>
{
    LongTaskAsync("A").ContinueWith((taskA) =>
    {
        LongTaskAsync("B").ContinueWith((taskB) => LongTaskAsync("C"));
    });
});