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,然后运行B和C,以并行方式运行
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"));
});
});