2014-07-03 9 views
9

Chciałbym zwrócić się do X różnych serwisów internetowych, z których każda zwróci true lub false.TPL oczekiwanie na zakończenie zadania z określoną wartością zwrotną.

Te zadania powinny być wykonywane równolegle i chciałbym poczekać na pierwszą, która zakończy z prawdziwą wartością. Kiedy otrzymam prawdziwą wartość, nie chcę czekać na wykonanie innych zadań.

W poniższym przykładzie t1 nie powinno być oczekiwany od t3 kończy pierwszy i zwraca true:

var t1 = Task.Run<bool>(() => 
{ 
    Thread.Sleep(5000); 
    Console.WriteLine("Task 1 Excecuted"); 
    return true; 
}, cts.Token); 

var t2 = Task.Run<bool>(() => 
{ 
    Console.WriteLine("Task 2 Executed"); 
    return false; 
}, cts.Token); 

var t3 = Task.Run<bool>(() => 
{ 
    Thread.Sleep(2000); 
    Console.WriteLine("Task 3 Executed"); 
    return true; 
}, cts.Token); 

Zasadniczo szukam Task.WhenAny z orzecznika, który oczywiście nie istnieje.

+1

http://stackoverflow.com/questions/14726854/task-parallel-library-waitany-with-specified-result –

+0

@AdamPlocher to pytanie jest mniej istotne w środowisku "oczekującym na awans". – i3arnon

+0

Wyczuwam gdzieś tutaj rozwiązanie TPL DataFlow, chciałbym mieć czas, aby usiąść i pobawić się nim! –

Odpowiedz

16

można po prostu użyć Task.WhenAny i orzecznika wielokrotnie, dopóki „prawo” zadaniem przyjdzie

async Task<T> WhenAny<T>(IEnumerable<Task<T>> tasks, Func<T, bool> predicate) 
{ 
    var taskList = tasks.ToList(); 
    Task<T> completedTask = null; 
    do 
    { 
     completedTask = await Task.WhenAny(taskList); 
     taskList.Remove(completedTask); 
    } while (!predicate(await completedTask) && taskList.Any()); 

    return completedTask == null ? default(T) : await completedTask; 
} 
8

Jedną opcją jest użycie Reactive Extensions. Załóżmy, że masz kolekcję zadań. Może to być zadania, które wymienione w pytaniu:

var tasks = new[] { t1, t2, t3 }; 

wykonanie zadań równolegle i powracającego po pierwsze zadania zwraca true użyć tego wyrażenia:

var result = await tasks 
    .Select(t => t.ToObservable()) 
    .Merge() 
    .FirstOrDefaultAsync(success => success); 

Zadania są przekształcone obserwowalne sekwencje, które każdy "strzela" raz po zakończeniu zadania. Sekwencje te są następnie łączone w jedną sekwencję, która następnie jest "przekształcana" z powrotem w coś, czego można oczekiwać za pomocą predykatu. W razie potrzeby można użyć bardziej skomplikowanego predykatu zamiast success => success.

Po tym można anulować pozostałe niedokończone zadania jeśli używasz CancellationTokenSource:

cts.Cancel(); 

Zmienna result będzie teraz albo true lub false i wszelkie pozostałe zadania nadano sygnał do odwołania.

Jeśli chcesz przetestować to z przykładowych zadań trzeba będzie modyfikować je lekko do korzystania Task.Delay zamiast Thread.Sleep włączyć zadanie ma być anulowane:

var t1 = Task.Run<bool>(async() => 
{ 
    await Task.Delay(TimeSpan.FromSeconds(1), cts.Token); 
    Console.WriteLine("Task 1 Excecuted"); 
    return false; 
}, cts.Token);