2016-07-10 7 views
2

zu implementieren Ich möchte mehrere Aufgaben asynchron ausführen, jede Aufgabe wird HTTP-Anforderung ausgeführt, die entweder Ausnahme auslösen oder sicher beenden kann. Ich muss beenden, wenn die erste Aufgabe erfolgreich abgeschlossen wurde oder wenn alle Aufgaben fehlgeschlagen sind. bitte Beratung.Wie Task.WhenAny() mit einem Prädikat

+0

selbst ausprobieren und fragen Sie nach speziellen Probleme auftreten. Atleast zeigen etwas Anstrengung. – CSharpie

Antwort

0
public static Task<T> GetFirstResult<T>(
    ICollection<Func<CancellationToken, Task<T>>> taskFactories, 
    Predicate<T> predicate) where T : class 
{ 
    var tcs = new TaskCompletionSource<T>(); 
    var cts = new CancellationTokenSource(); 

    int completedCount = 0; 
    // in case you have a lot of tasks you might need to throttle them 
    //(e.g. so you don't try to send 99999999 requests at the same time) 
    // see: http://stackoverflow.com/a/25877042/67824 
    foreach (var taskFactory in taskFactories) 
    { 
     taskFactory(cts.Token).ContinueWith(t => 
     { 
      if (t.Exception != null) 
      { 
       Console.WriteLine($"Task completed with exception: {t.Exception}"); 
      } 
      else if (predicate(t.Result)) 
      { 
       cts.Cancel(); 
       tcs.TrySetResult(t.Result); 
      } 

      if (Interlocked.Increment(ref completedCount) == taskFactories.Count) 
      { 
       tcs.SetException(new InvalidOperationException("All tasks failed")); 
      } 

     }, cts.Token); 
    } 

    return tcs.Task; 
} 

Verwendungsbeispiel:

using System.Net.Http; 
var client = new HttpClient(); 
var response = await GetFirstResult(
    new Func<CancellationToken, Task<HttpResponseMessage>>[] 
    { 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
     ct => client.GetAsync("http://microsoft123456.com", ct), 
    }, 
    rm => rm.IsSuccessStatusCode); 
Console.WriteLine($"Successful response: {response}"); 
+2

Vielen Dank für die schnelle Reaktion und die großartigen technischen Lösungen. Sie haben mir sehr geholfen. –

+0

Ich bin nur der Schützling - du solltest wirklich meinem Mentor danken (und upvote!) Http://stackoverflow.com/a/38289396/67824;) –

2
public static Task<Task<T>> WhenFirst<T>(IEnumerable<Task<T>> tasks, Func<Task<T>, bool> predicate) 
{ 
    if (tasks == null) throw new ArgumentNullException(nameof(tasks)); 
    if (predicate == null) throw new ArgumentNullException(nameof(predicate)); 

    var tasksArray = (tasks as IReadOnlyList<Task<T>>) ?? tasks.ToArray(); 
    if (tasksArray.Count == 0) throw new ArgumentException("Empty task list", nameof(tasks)); 
    if (tasksArray.Any(t => t == null)) throw new ArgumentException("Tasks contains a null reference", nameof(tasks)); 

    var tcs = new TaskCompletionSource<Task<T>>(); 
    var count = tasksArray.Count; 

    Action<Task<T>> continuation = t => 
     { 
      if (predicate(t)) 
      { 
       tcs.TrySetResult(t); 
      } 
      if (Interlocked.Decrement(ref count) == 0) 
      { 
       tcs.TrySetResult(null); 
      } 
     }; 

    foreach (var task in tasksArray) 
    { 
     task.ContinueWith(continuation); 
    } 

    return tcs.Task; 
} 

Verwendungsbeispiel:

var task = await WhenFirst(tasks, t => t.Status == TaskStatus.RanToCompletion); 

if (task != null) 
    var value = await task; 

Beachten Sie, dass dies nicht Ausnahmen gescheiterter Aufgaben ausbreitet (wie WhenAny nicht).

Sie können auch eine Version für das nicht generische Task erstellen.

2

Warten Sie auf eine Aufgabe und geben Sie die Aufgabe zurück, wenn die Bedingung erfüllt ist. Andernfalls warten Sie erneut auf die anderen Aufgaben, bis keine weitere Aufgabe mehr wartet.

public static async Task<Task> WhenAny(IEnumerable<Task> tasks, Predicate<Task> condition) 
{ 
    var tasklist = tasks.ToList(); 
    while (tasklist.Count > 0) 
    { 
     var task = await Task.WhenAny(tasklist); 
     if (condition(task)) 
      return task; 
     tasklist.Remove(task); 
    } 
    return null; 
} 

einfache Prüfung für das

var tasks = new List<Task> { 
    Task.FromException(new Exception()), 
    Task.FromException(new Exception()), 
    Task.FromException(new Exception()), 
    Task.CompletedTask, }; 

var completedTask = WhenAny(tasks, t => t.Status == TaskStatus.RanToCompletion).Result; 

if (tasks.IndexOf(completedTask) != 3) 
    throw new Exception("not expected"); 
Verwandte Themen