2017-12-07 4 views
0

정말 미학적 인 질문입니다. 각각 다른 지연을 가지고,C# async/작업 완료 대기 중으로 작업 완료 대기

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"); 
    } 
} 

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"); 
} 

이도 올바른 패턴하지 않을 수도 Poll 방법의 각 루프을 제거하는 연속을 구성하는 방법이 있지만,보다 더 나은 것 같다 3 개의 무한 루프가 있습니다.

Task.WhenAny([A,B,C]) => 
// recreate any complete task as soon as it returns 
// and await the new "continuation"? 
+1

왜 그냥'기다리고 Task.WhenAll (this.PollA (CT), this.PollB (CT), 이 .PollC (ct))'? 그러나 확실히 당신은 'while (! ct.IsCancellationRequested)이 Task.WhenAny (this.PollA (ct), this.PollB (ct), this.PollC (ct))'를 기다리고 있습니다. –

답변

1

나는 아마도 스택 오버플로가 발생할 가능성이 높으므로 사용하지 않는 것이 좋습니다. 아마 루프가 더 나은 옵션 인 이유를 보여줄 수 있습니다.

나는 현실 세계에서 당신의 예를 정말로 이해하지 못한다. 오랫동안 실행되는 거의 모든 코드가 유한 루프에서 수행되므로 각 루프 반복 후에 취소를 확인하는 것이 나에게 좋은 생각 인 것 같습니다.

작업이 취소 될 때까지 코드를 무한히 실행하기를 원하지 않는 한, 내 미적 솔루션은 길게 남겨두면 스택 오버플로가 발생할 수 있지만이 코드를 사용하면 재미가 없다. 다음으로 호출되는 코드에 대한 자료

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); 
     } 
    } 
} 

은 다음과 같습니다 :

은 내가 확장 방법을 만들어

나는 작업에서 다시 각각의 방법을 포장의 점을 볼 해달라고하지만
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); 

.운영. 그래서 나는 또한 단지

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); 
+0

mmh, 그게 작동합니다 - 비록 그 패턴이 깨진 것 같아요, 그리고 그것은 단지 루프를 사용하는 것이 좋습니다 ... 감사합니다. 내 생각에 스택의 고유 한 문제에 대해서도 정확하다고 생각합니다 ... – Jim

+1

@Jim 예, 부러졌습니다. 대체 방법이 더 나쁘기 때문에 루프를 사용해야하는 이유가 여기에 있습니다. –

+0

사용자 정의 Awaitable 그 트릭을 할 수도 있지만, 난장판처럼 보일 것 같아. – Jim

0

당신은이 같은 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); 
    } 
}