我怎样才能按顺序而不是并行地等待 C# 任务?
How can I await C# tasks in order, and not in parallel?
我有一组异步测试,运行 在外部硬件上。
我可以 运行 它们按顺序排列,但因为所有这些测试都有副作用,我希望能够将它们打乱并 运行 一遍又一遍。
当我把它们放在一个列表中并尝试等待每个之后,它们都是并行 运行 而不是 运行 1 乘 1。
我希望能够打乱列表,然后 运行 将它们全部按顺序排列。
我怎样才能 运行 测试 (List<Task<bool>
) 按顺序排列,同时将它们保存在列表中以便我可以随机排列列表?
private async void RunVisca()
{
Ptz ptz = new Ptz(SelectedComPort, (byte)(1), SelectedBaudRate, Node.DeviceType.PTZ);
UpdateResults("VISCA TEST START\n\n");
// Method 1: Correct - Runs in order
await VTestDriveClosedLoop(ptz, true);
await VTestDriveClosedLoop(ptz, false);
await VTestLimitsCl(ptz, 125, 20);
await VTestLimitsOl(ptz, 125, 20);
// Method 2: Incorrect - Runs in parallel
List<Task<bool>> tests = new List<Task<bool>>();
tests.Add(VTestDriveClosedLoop(ptz, true));
tests.Add(VTestDriveClosedLoop(ptz, false));
tests.Add(VTestLimitsCl(ptz, 125, 20));
tests.Add(VTestLimitsOl(ptz, 125, 20));
// Tasks all run in parallel here - should they not run in order?
foreach(Task<bool> test in tests)
{
_ = await test.ConfigureAwait(true);
}
UpdateResults("\nTEST COMPLETE\n\n");
}
您可以创建一个列表,在其中随机化您想要的 运行 事物的顺序,然后相应地调用创建任务的函数。例如:
static async Task RunTasks()
{
Random rng = new Random();
var order = new List<int>() { 1, 2 };
var shuffledOrder = order.OrderBy(a => rng.Next()).ToList();
foreach (var i in shuffledOrder)
{
switch (i)
{
case 1:
await LoadFile1();
break;
case 2:
await LoadFile2();
break;
}
}
}
Task-based Asynchronous Pattern (TAP) pattern 意味着调用一个 async
方法,return 是一个 Task
,将 return 一个 热门任务.
一个处于热状态的Task
已经启动了。这可以通过检查 Task.Status
属性 来证明,它永远不会是 TaskStatus.Created
.
一个 冷任务 将有一个 Task.Status
属性 的 TaskStatus.Created
并且只会在 运行 开始时 Start()
在实例上被调用。
创建冷任务的唯一方法是使用 Task
/Task<T>
各自的 public 构造函数。
由于您的任务是通过异步方法 return 编辑的,因此当它们 return 发送给您时,它们已经 运行ning 了。
为了 运行 它们是按顺序而不是并行的,除非上一个任务已经完成,否则你不能一个接一个地创建它们。为确保这一点,您必须单独 await
它们。
这导致方法 1 是 运行 热门任务 的正确方法,而不是并行。
await VTestDriveClosedLoop(ptz, true);
await VTestDriveClosedLoop(ptz, false);
await VTestLimitsCl(ptz, 125, 20);
await VTestLimitsOl(ptz, 125, 20);
方法 2/3 不正确,因为它将 运行 您的任务并行进行。
您正在将 return 从您的 async
方法编辑的热门任务添加到您的列表中,而不等待它们。在您继续将下一个任务添加到列表之前,他们将已经开始运行宁。
第一个任务未等待因此第二个任务将在之后立即开始,无论任务 1 是否完成。
第二个任务没有等待,所以第三个任务等等
使用方法 2/3,它们将始终 运行 并行。
tests.Add(VTestDriveClosedLoop(ptz, true));
tests.Add(VTestDriveClosedLoop(ptz, false));
真正的问题是如何 运行 冷任务 顺序,以便我们可以将它们存储在列表中,同时利用延迟执行,直到我们遍历我们的列表。
解决方案是改为存储委托,这将触发以下任务构造函数:
public Task (Func<TResult> function);
这将创建冷任务,让您在调用 Func<Task<bool>>
时 运行 Task
:
var tests = new List<Func<Task<bool>>>();
tests.Add(() => VTestDriveClosedLoop(ptz, true));
tests.Add(() => VTestDriveClosedLoop(ptz, false));
tests.Add(() => VTestLimitsCl(ptz, 125, 20));
tests.Add(() => VTestLimitsOl(ptz, 125, 20));
foreach (var test in tests) {
await test();
}
下面的演示程序将清楚地显示 运行ning 任务的顺序与并行。
class Program
{
private static async Task Main(string[] args)
{
// 1... 2... 3... 4...
var tasksThatRunInOrder = new List<Func<Task<bool>>>();
tasksThatRunInOrder.Add(() => Test("1"));
tasksThatRunInOrder.Add(() => Test("2"));
tasksThatRunInOrder.Add(() => Test("3"));
tasksThatRunInOrder.Add(() => Test("4"));
foreach (var test in tasksThatRunInOrder)
await test();
// 1, 2, 3, 4
var testsThatRunInParallel = new List<Task<bool>>();
testsThatRunInParallel.Add(Test("1"));
testsThatRunInParallel.Add(Test("2"));
testsThatRunInParallel.Add(Test("3"));
testsThatRunInParallel.Add(Test("4"));
foreach (var test in testsThatRunInParallel)
await test;
}
private static async Task<bool> Test(string x)
{
Console.WriteLine(x);
await Task.Delay(1000);
return true;
}
}
ContinueWith
的调用链如何?
Task.Run(async () =>
{
await VTestDriveClosedLoop(ptz, true);
}).ContinueWith(async (_) =>
{
await VTestDriveClosedLoop(ptz, false);
}).ContinueWith(async (_) =>
{
await VTestLimitsCl(ptz, 125, 20);
}).ContinueWith(async (_) =>
{
await VTestLimitsOl(ptz, 125, 20);
});
任务在创建时开始。由于您是在不等待任何一个的情况下同时创建所有这些,因此它们将以不确定的并行方式执行。我想也许您想改为存储代表。这些在调用之前不会执行。
var tests = new List<Func<Task<bool>>>();
tests.Add( () => VTestDriveClosedLoop(ptz, true) );
tests.Add( () => VTestDriveClosedLoop(ptz, false) );
tests.Add( () => VTestLimitsCl(ptz, 125, 20) );
tests.Add( () => VTestLimitsOl(ptz, 125, 20) );
foreach(var test in tests)
{
_ = await test();
}
尝试等待而不分配给一次性变量。
我有一组异步测试,运行 在外部硬件上。
我可以 运行 它们按顺序排列,但因为所有这些测试都有副作用,我希望能够将它们打乱并 运行 一遍又一遍。
当我把它们放在一个列表中并尝试等待每个之后,它们都是并行 运行 而不是 运行 1 乘 1。
我希望能够打乱列表,然后 运行 将它们全部按顺序排列。
我怎样才能 运行 测试 (List<Task<bool>
) 按顺序排列,同时将它们保存在列表中以便我可以随机排列列表?
private async void RunVisca()
{
Ptz ptz = new Ptz(SelectedComPort, (byte)(1), SelectedBaudRate, Node.DeviceType.PTZ);
UpdateResults("VISCA TEST START\n\n");
// Method 1: Correct - Runs in order
await VTestDriveClosedLoop(ptz, true);
await VTestDriveClosedLoop(ptz, false);
await VTestLimitsCl(ptz, 125, 20);
await VTestLimitsOl(ptz, 125, 20);
// Method 2: Incorrect - Runs in parallel
List<Task<bool>> tests = new List<Task<bool>>();
tests.Add(VTestDriveClosedLoop(ptz, true));
tests.Add(VTestDriveClosedLoop(ptz, false));
tests.Add(VTestLimitsCl(ptz, 125, 20));
tests.Add(VTestLimitsOl(ptz, 125, 20));
// Tasks all run in parallel here - should they not run in order?
foreach(Task<bool> test in tests)
{
_ = await test.ConfigureAwait(true);
}
UpdateResults("\nTEST COMPLETE\n\n");
}
您可以创建一个列表,在其中随机化您想要的 运行 事物的顺序,然后相应地调用创建任务的函数。例如:
static async Task RunTasks()
{
Random rng = new Random();
var order = new List<int>() { 1, 2 };
var shuffledOrder = order.OrderBy(a => rng.Next()).ToList();
foreach (var i in shuffledOrder)
{
switch (i)
{
case 1:
await LoadFile1();
break;
case 2:
await LoadFile2();
break;
}
}
}
Task-based Asynchronous Pattern (TAP) pattern 意味着调用一个 async
方法,return 是一个 Task
,将 return 一个 热门任务.
一个处于热状态的Task
已经启动了。这可以通过检查 Task.Status
属性 来证明,它永远不会是 TaskStatus.Created
.
一个 冷任务 将有一个 Task.Status
属性 的 TaskStatus.Created
并且只会在 运行 开始时 Start()
在实例上被调用。
创建冷任务的唯一方法是使用 Task
/Task<T>
各自的 public 构造函数。
由于您的任务是通过异步方法 return 编辑的,因此当它们 return 发送给您时,它们已经 运行ning 了。
为了 运行 它们是按顺序而不是并行的,除非上一个任务已经完成,否则你不能一个接一个地创建它们。为确保这一点,您必须单独 await
它们。
这导致方法 1 是 运行 热门任务 的正确方法,而不是并行。
await VTestDriveClosedLoop(ptz, true);
await VTestDriveClosedLoop(ptz, false);
await VTestLimitsCl(ptz, 125, 20);
await VTestLimitsOl(ptz, 125, 20);
方法 2/3 不正确,因为它将 运行 您的任务并行进行。
您正在将 return 从您的 async
方法编辑的热门任务添加到您的列表中,而不等待它们。在您继续将下一个任务添加到列表之前,他们将已经开始运行宁。
第一个任务未等待因此第二个任务将在之后立即开始,无论任务 1 是否完成。
第二个任务没有等待,所以第三个任务等等
使用方法 2/3,它们将始终 运行 并行。
tests.Add(VTestDriveClosedLoop(ptz, true));
tests.Add(VTestDriveClosedLoop(ptz, false));
真正的问题是如何 运行 冷任务 顺序,以便我们可以将它们存储在列表中,同时利用延迟执行,直到我们遍历我们的列表。
解决方案是改为存储委托,这将触发以下任务构造函数:
public Task (Func<TResult> function);
这将创建冷任务,让您在调用 Func<Task<bool>>
时 运行 Task
:
var tests = new List<Func<Task<bool>>>();
tests.Add(() => VTestDriveClosedLoop(ptz, true));
tests.Add(() => VTestDriveClosedLoop(ptz, false));
tests.Add(() => VTestLimitsCl(ptz, 125, 20));
tests.Add(() => VTestLimitsOl(ptz, 125, 20));
foreach (var test in tests) {
await test();
}
下面的演示程序将清楚地显示 运行ning 任务的顺序与并行。
class Program
{
private static async Task Main(string[] args)
{
// 1... 2... 3... 4...
var tasksThatRunInOrder = new List<Func<Task<bool>>>();
tasksThatRunInOrder.Add(() => Test("1"));
tasksThatRunInOrder.Add(() => Test("2"));
tasksThatRunInOrder.Add(() => Test("3"));
tasksThatRunInOrder.Add(() => Test("4"));
foreach (var test in tasksThatRunInOrder)
await test();
// 1, 2, 3, 4
var testsThatRunInParallel = new List<Task<bool>>();
testsThatRunInParallel.Add(Test("1"));
testsThatRunInParallel.Add(Test("2"));
testsThatRunInParallel.Add(Test("3"));
testsThatRunInParallel.Add(Test("4"));
foreach (var test in testsThatRunInParallel)
await test;
}
private static async Task<bool> Test(string x)
{
Console.WriteLine(x);
await Task.Delay(1000);
return true;
}
}
ContinueWith
的调用链如何?
Task.Run(async () =>
{
await VTestDriveClosedLoop(ptz, true);
}).ContinueWith(async (_) =>
{
await VTestDriveClosedLoop(ptz, false);
}).ContinueWith(async (_) =>
{
await VTestLimitsCl(ptz, 125, 20);
}).ContinueWith(async (_) =>
{
await VTestLimitsOl(ptz, 125, 20);
});
任务在创建时开始。由于您是在不等待任何一个的情况下同时创建所有这些,因此它们将以不确定的并行方式执行。我想也许您想改为存储代表。这些在调用之前不会执行。
var tests = new List<Func<Task<bool>>>();
tests.Add( () => VTestDriveClosedLoop(ptz, true) );
tests.Add( () => VTestDriveClosedLoop(ptz, false) );
tests.Add( () => VTestLimitsCl(ptz, 125, 20) );
tests.Add( () => VTestLimitsOl(ptz, 125, 20) );
foreach(var test in tests)
{
_ = await test();
}
尝试等待而不分配给一次性变量。