如何让 Task.ContinueWith 依次执行异步方法?
How can I get Task.ContinueWith to execute async methods in sequence?
给定以下代码,我希望操作按顺序执行
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp32
{
class Program
{
static SpinLock Lock = new SpinLock();
static Task Head = Task.CompletedTask;
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
CreateTask(1);
CreateTask(2);
CreateTask(3);
CreateTask(4);
await Task.Delay(1000); // Half way through executing
CreateTask(5);
CreateTask(6);
CreateTask(7);
CreateTask(8);
await Task.Delay(5000);
}
static void CreateTask(int i)
{
bool lockTaken = false;
while (!lockTaken)
Lock.Enter(ref lockTaken);
try
{
Head = Head.ContinueWith(_ => DoActionAsync(i));
}
finally
{
Lock.Exit();
}
}
static async Task DoActionAsync(int i)
{
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Creating " + i);
await Task.Delay(1000);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Finished " + i);
}
}
}
但实际输出是
Hello World!
09:08:06: Creating 1
09:08:06: Creating 2
09:08:06: Creating 3
09:08:06: Creating 4
09:08:07: Finished 2
09:08:07: Finished 3
09:08:07: Finished 4
09:08:07: Creating 5
09:08:07: Finished 1
09:08:07: Creating 6
09:08:07: Creating 7
09:08:07: Creating 8
09:08:08: Finished 7
09:08:08: Finished 6
09:08:08: Finished 8
09:08:08: Finished 5
为什么 2 在 1 之前完成,而不是在 1 完成之后才开始? (我想这是同一个原因,因为他们都同时完成了)。
.ContinueWith(_ => DoActionAsync(i))
Returns Task
(或者特别是 Task<Task>
)在 DoActionAsync
return 时完成。 DoActionAsync 将在第一次等待时 return,从而完成外部 Task
并允许下一个操作开始。
因此它将 运行 每个“创建”顺序地进行,但是“完成”将 运行 异步进行。
一个简单的解决方案是使用 LimitedconcurrencyTaskScheduler 来安排所有任务。或者只是创建一个操作队列并让一个线程按顺序处理队列。
那是因为任务是嵌套的。您正在任务中创建任务而不展开它们。
试试这个:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp32
{
class Program
{
static SpinLock Lock = new SpinLock();
static Task Head = Task.CompletedTask;
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
CreateTask(1);
CreateTask(2);
CreateTask(3);
CreateTask(4);
await Task.Delay(1000); // Half way through executing
CreateTask(5);
CreateTask(6);
CreateTask(7);
CreateTask(8);
await Task.Delay(5000);
}
static void CreateTask(int i)
{
bool lockTaken = false;
while (!lockTaken)
Lock.Enter(ref lockTaken);
try
{
Head = Head.ContinueWith(_ => DoActionAsync(i)).Unwrap();
}
finally
{
Lock.Exit();
}
}
static async Task DoActionAsync(int i)
{
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Creating " + i);
await Task.Delay(1000);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Finished " + i);
}
}
}
注意 Unwrap()
您“滥用”的重载是一般的 Func<object, T>
重载,您将 Task 分配给类型 T 而不是结果类型。通常 ContinueWith
使用同步委托。
.NET 考虑到了这一点,他们创建了 Unwrap()
,它解包了嵌套任务。
给定以下代码,我希望操作按顺序执行
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp32
{
class Program
{
static SpinLock Lock = new SpinLock();
static Task Head = Task.CompletedTask;
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
CreateTask(1);
CreateTask(2);
CreateTask(3);
CreateTask(4);
await Task.Delay(1000); // Half way through executing
CreateTask(5);
CreateTask(6);
CreateTask(7);
CreateTask(8);
await Task.Delay(5000);
}
static void CreateTask(int i)
{
bool lockTaken = false;
while (!lockTaken)
Lock.Enter(ref lockTaken);
try
{
Head = Head.ContinueWith(_ => DoActionAsync(i));
}
finally
{
Lock.Exit();
}
}
static async Task DoActionAsync(int i)
{
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Creating " + i);
await Task.Delay(1000);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Finished " + i);
}
}
}
但实际输出是
Hello World!
09:08:06: Creating 1
09:08:06: Creating 2
09:08:06: Creating 3
09:08:06: Creating 4
09:08:07: Finished 2
09:08:07: Finished 3
09:08:07: Finished 4
09:08:07: Creating 5
09:08:07: Finished 1
09:08:07: Creating 6
09:08:07: Creating 7
09:08:07: Creating 8
09:08:08: Finished 7
09:08:08: Finished 6
09:08:08: Finished 8
09:08:08: Finished 5
为什么 2 在 1 之前完成,而不是在 1 完成之后才开始? (我想这是同一个原因,因为他们都同时完成了)。
.ContinueWith(_ => DoActionAsync(i))
Returns Task
(或者特别是 Task<Task>
)在 DoActionAsync
return 时完成。 DoActionAsync 将在第一次等待时 return,从而完成外部 Task
并允许下一个操作开始。
因此它将 运行 每个“创建”顺序地进行,但是“完成”将 运行 异步进行。
一个简单的解决方案是使用 LimitedconcurrencyTaskScheduler 来安排所有任务。或者只是创建一个操作队列并让一个线程按顺序处理队列。
那是因为任务是嵌套的。您正在任务中创建任务而不展开它们。
试试这个:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp32
{
class Program
{
static SpinLock Lock = new SpinLock();
static Task Head = Task.CompletedTask;
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
CreateTask(1);
CreateTask(2);
CreateTask(3);
CreateTask(4);
await Task.Delay(1000); // Half way through executing
CreateTask(5);
CreateTask(6);
CreateTask(7);
CreateTask(8);
await Task.Delay(5000);
}
static void CreateTask(int i)
{
bool lockTaken = false;
while (!lockTaken)
Lock.Enter(ref lockTaken);
try
{
Head = Head.ContinueWith(_ => DoActionAsync(i)).Unwrap();
}
finally
{
Lock.Exit();
}
}
static async Task DoActionAsync(int i)
{
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Creating " + i);
await Task.Delay(1000);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss") + ": Finished " + i);
}
}
}
注意 Unwrap()
您“滥用”的重载是一般的 Func<object, T>
重载,您将 Task 分配给类型 T 而不是结果类型。通常 ContinueWith
使用同步委托。
.NET 考虑到了这一点,他们创建了 Unwrap()
,它解包了嵌套任务。