链接 c# async / await 任务,在它们完成时重新创建它们
chaining c# async / await tasks, recreating them as they complete
真的审美问题。
鉴于此代码(轮询不可避免):
protected override async Task ExecuteAsync(CancellationToken ct)
{
// move the loop here somehow?
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct), ct),
Task.Run(async () => await this.PollB(ct), ct),
Task.Run(async () => await this.PollC(ct), ct))
.ConfigureAwait(false);
}
目前的轮询方法是这样的,每个都有不同的延迟。
private async Task Poll(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
}
有没有一种方法可以构造一个延续在每个Poll
方法[=15]中删除循环 =]
private async Task Poll(CancellationToken ct)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
这甚至可能不是正确的模式,但它似乎比拥有三个无限循环要好。
Task.WhenAny([A,B,C]) =>
// recreate any complete task as soon as it returns
// and await the new "continuation"?
我有一个美学解决方案,可能不建议使用它,因为它最终可能会导致堆栈溢出。
它可能说明了为什么循环是更好的选择。
我必须承认在现实世界中并不能真正理解你的例子。
在我看来,几乎所有长时间执行的代码都会在有限循环中执行,因此在每次循环迭代后检查取消对我来说听起来是个好主意。
除非你想让你的代码无限地 运行 直到任务被取消,在这种情况下,我的审美解决方案如果放得太久可能会导致堆栈溢出,但它很有趣 none提出此代码的人越少。
我创建了一个扩展方法:
public static class Extensions
{
public static async Task ContinueWithInfinitly(this Task task, Func<Task> continuationAction, CancellationToken cancellationToken)
{
await task;
if (!cancellationToken.IsCancellationRequested)
{
var newTask = continuationAction.Invoke();
await newTask.ContinueWithInfinitly(continuationAction, cancellationToken);
}
}
}
根据你的代码将调用哪个Base,如下所示:
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct)),
Task.Run(async () => await this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct)),
Task.Run(async () => await this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct)))
.ConfigureAwait(false);
虽然我不明白在 Task.Run 中再次包装每个方法的意义。
所以我也可以
await Task.WhenAll(
this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct),
this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct),
this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct))
.ConfigureAwait(false);
您可以这样使用 Task.WhenAny
:
private async Task<Tuple<string, int>> Poll(string type, int delay, CancellationToken ct) {
await Task.Delay(Math.Max(1000, delay), ct);
Console.WriteLine($"poll {type} status");
// return input arguments back
return Tuple.Create(type, delay);
}
private async Task PollAll(CancellationToken ct) {
var tasks = new []
{
Poll("A", 3000, ct),
Poll("B", 2000, ct),
Poll("C", 1000, ct)
};
while (!ct.IsCancellationRequested) {
var completed = await Task.WhenAny(tasks);
var index = Array.IndexOf(tasks, completed);
// await to throw exceptions if any
await completed;
// replace with new task with the same arguments
tasks[index] = Poll(completed.Result.Item1, completed.Result.Item2, ct);
}
}
真的审美问题。
鉴于此代码(轮询不可避免):
protected override async Task ExecuteAsync(CancellationToken ct)
{
// move the loop here somehow?
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct), ct),
Task.Run(async () => await this.PollB(ct), ct),
Task.Run(async () => await this.PollC(ct), ct))
.ConfigureAwait(false);
}
目前的轮询方法是这样的,每个都有不同的延迟。
private async Task Poll(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
}
有没有一种方法可以构造一个延续在每个Poll
方法[=15]中删除循环 =]
private async Task Poll(CancellationToken ct)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
这甚至可能不是正确的模式,但它似乎比拥有三个无限循环要好。
Task.WhenAny([A,B,C]) =>
// recreate any complete task as soon as it returns
// and await the new "continuation"?
我有一个美学解决方案,可能不建议使用它,因为它最终可能会导致堆栈溢出。 它可能说明了为什么循环是更好的选择。
我必须承认在现实世界中并不能真正理解你的例子。 在我看来,几乎所有长时间执行的代码都会在有限循环中执行,因此在每次循环迭代后检查取消对我来说听起来是个好主意。
除非你想让你的代码无限地 运行 直到任务被取消,在这种情况下,我的审美解决方案如果放得太久可能会导致堆栈溢出,但它很有趣 none提出此代码的人越少。
我创建了一个扩展方法:
public static class Extensions
{
public static async Task ContinueWithInfinitly(this Task task, Func<Task> continuationAction, CancellationToken cancellationToken)
{
await task;
if (!cancellationToken.IsCancellationRequested)
{
var newTask = continuationAction.Invoke();
await newTask.ContinueWithInfinitly(continuationAction, cancellationToken);
}
}
}
根据你的代码将调用哪个Base,如下所示:
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct)),
Task.Run(async () => await this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct)),
Task.Run(async () => await this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct)))
.ConfigureAwait(false);
虽然我不明白在 Task.Run 中再次包装每个方法的意义。 所以我也可以
await Task.WhenAll(
this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct),
this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct),
this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct))
.ConfigureAwait(false);
您可以这样使用 Task.WhenAny
:
private async Task<Tuple<string, int>> Poll(string type, int delay, CancellationToken ct) {
await Task.Delay(Math.Max(1000, delay), ct);
Console.WriteLine($"poll {type} status");
// return input arguments back
return Tuple.Create(type, delay);
}
private async Task PollAll(CancellationToken ct) {
var tasks = new []
{
Poll("A", 3000, ct),
Poll("B", 2000, ct),
Poll("C", 1000, ct)
};
while (!ct.IsCancellationRequested) {
var completed = await Task.WhenAny(tasks);
var index = Array.IndexOf(tasks, completed);
// await to throw exceptions if any
await completed;
// replace with new task with the same arguments
tasks[index] = Poll(completed.Result.Item1, completed.Result.Item2, ct);
}
}