web-dev-qa-db-ja.com

タイムアウト付きの非同期Task.WhenAll

新しい非同期dotnet 4.5ライブラリには、Task.WhenAllメソッドにタイムアウトを設定する方法があります。いくつかのソースを取得し、5秒後に停止して、終了していないソースをスキップしたいです。

55
broersa

Task.Delay()を使用して、結果のTaskTask.WhenAny()と組み合わせることができます。

await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(timeout));

タイムアウトの場合に完了したタスクを収集する場合:

var completedResults =
  tasks
  .Where(t => t.Status == TaskStatus.RanToCompletion)
  .Select(t => t.Result)
  .ToList();
76
svick

また、 例外処理権限 も行う、より明確で堅牢なオプションは、 _Task.WhenAny_ 各タスクで タイムアウトタスク と一緒に、完了したすべてのタスクを実行し、タイムアウトタスクを除外します。すべての結果を収集するには、_Task.Result_の代わりにawait Task.WhenAll()を使用します。

完全な実用的なソリューションを次に示します。

_static async Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks, TimeSpan timeout)
{
    var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult));
    var completedTasks = 
        (await Task.WhenAll(tasks.Select(task => Task.WhenAny(task, timeoutTask)))).
        Where(task => task != timeoutTask);
    return await Task.WhenAll(completedTasks);
}
_
19
i3arnon

Microsoftの Task-Based Asynchronous Pattern Overview の「Early Bailout」および「Task.Delay」セクションを確認してください。

早期救済。 t1で表される操作は、別のタスクt2を使用してWhenAnyにグループ化でき、WhenAnyタスクで待機できます。 t2は、タイムアウト、キャンセル、またはt1が完了する前にWhenAnyタスクを完了させるその他の信号を表します。

9
David Peden

タイムアウトに加えて、キャンセルをチェックします。これは、Webアプリを構築している場合に便利です。

public static async Task WhenAll(
    IEnumerable<Task> tasks, 
    int millisecondsTimeOut,
    CancellationToken cancellationToken)
{
    using(Task timeoutTask = Task.Delay(millisecondsTimeOut))
    using(Task cancellationMonitorTask = Task.Delay(-1, cancellationToken))
    {
        Task completedTask = await Task.WhenAny(
            Task.WhenAll(tasks), 
            timeoutTask, 
            cancellationMonitorTask
        );

        if (completedTask == timeoutTask)
        {
            throw new TimeoutException();
        }
        if (completedTask == cancellationMonitorTask)
        {
            throw new OperationCanceledException();
        }
        await completedTask;
    }
}
2
Tony

あなたが説明することは非常に一般的な要求のように見えますが、この例はどこにも見つかりませんでした。そして、私は多くを検索しました...私は最終的に以下を作成しました:

TimeSpan timeout = TimeSpan.FromSeconds(5.0);

Task<Task>[] tasksOfTasks =
{
    Task.WhenAny(SomeTaskAsync("a"), Task.Delay(timeout)),
    Task.WhenAny(SomeTaskAsync("b"), Task.Delay(timeout)),
    Task.WhenAny(SomeTaskAsync("c"), Task.Delay(timeout))
};

Task[] completedTasks = await Task.WhenAll(tasksOfTasks);

List<MyResult> = completedTasks.OfType<Task<MyResult>>().Select(task => task.Result).ToList();

ここでは、Task <MyResult>を返すSomeTaskAsyncメソッドを想定しています。

CompletedTasksのメンバーから見ると、MyResult型のタスクのみが、なんとか時計を打ち負かすことができる独自のタスクです。 Task.Delayは異なる型を返します。これにはタイピングで多少の妥協が必要ですが、それでも美しく、非常に簡単に機能します。

(もちろん、配列はクエリ+ ToArrayを使用して動的に構築できます)。

  • この実装では、キャンセルトークンを受信するためにSomeTaskAsyncを必要としないことに注意してください。
2
Erez Cohen

@ i3arnonの回答の結果バージョンを無効にし、コメントと、拡張thisを使用するように最初の引数を変更します。

また、TimeSpan.FromMilliseconds(millisecondsTimeout)を使用してintとしてタイムアウトを指定して、他のタスクメソッドと一致する転送メソッドも用意しています。

public static async Task WhenAll(this IEnumerable<Task> tasks, TimeSpan timeout)
{
  // Create a timeout task.
  var timeoutTask = Task.Delay(timeout);

  // Get the completed tasks made up of...
  var completedTasks =
  (
    // ...all tasks specified
    await Task.WhenAll(tasks

    // Now finish when its task has finished or the timeout task finishes
    .Select(task => Task.WhenAny(task, timeoutTask)))
  )
  // ...but not the timeout task
  .Where(task => task != timeoutTask);

  // And wait for the internal WhenAll to complete.
  await Task.WhenAll(completedTasks);
}
1
kjhf

Svickの答えに加えて、いくつかのタスクが完了するのを待つ必要があるが、待っている間に他の何かを処理する必要がある場合、次のように機能します:

Task[] TasksToWaitFor = //Your tasks
TimeSpan Timeout = TimeSpan.FromSeconds( 30 );

while( true )
{
    await Task.WhenAny( Task.WhenAll( TasksToWaitFor ), Task.Delay( Timeout ) );
    if( TasksToWaitFor.All( a => a.IsCompleted ) )
        break;

    //Do something else here
}
0
Simon Mattes

私は必要なことを行う次のコードに行きました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Json;
using System.Threading;

namespace MyAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            var cts = new CancellationTokenSource();
            Console.WriteLine("Start Main");
            List<Task<List<MyObject>>> listoftasks = new List<Task<List<MyObject>>>();
            listoftasks.Add(GetGoogle(cts));
            listoftasks.Add(GetTwitter(cts));
            listoftasks.Add(GetSleep(cts));
            listoftasks.Add(GetxSleep(cts));

            List<MyObject>[] arrayofanswers = Task.WhenAll(listoftasks).Result;
            List<MyObject> answer = new List<MyObject>();
            foreach (List<MyObject> answers in arrayofanswers)
            {
                answer.AddRange(answers);
            }
            foreach (MyObject o in answer)
            {
                Console.WriteLine("{0} - {1}", o.name, o.Origin);
            }
            Console.WriteLine("Press <Enter>");
            Console.ReadLine();
        } 

        static async Task<List<MyObject>> GetGoogle(CancellationTokenSource cts) 
        {
            try
            {
                Console.WriteLine("Start GetGoogle");
                List<MyObject> l = new List<MyObject>();
                var client = new HttpClient();
                Task<HttpResponseMessage> awaitable = client.GetAsync("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=broersa", cts.Token);
                HttpResponseMessage res = await awaitable;
                Console.WriteLine("After GetGoogle GetAsync");
                dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                Console.WriteLine("After GetGoogle ReadAsStringAsync");
                foreach (var r in data.responseData.results)
                {
                    l.Add(new MyObject() { name = r.titleNoFormatting, Origin = "google" });
                }
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List<MyObject>();
            }
        }

        static async Task<List<MyObject>> GetTwitter(CancellationTokenSource cts)
        {
            try
            {
                Console.WriteLine("Start GetTwitter");
                List<MyObject> l = new List<MyObject>();
                var client = new HttpClient();
                Task<HttpResponseMessage> awaitable = client.GetAsync("http://search.Twitter.com/search.json?q=broersa&rpp=5&include_entities=true&result_type=mixed",cts.Token);
                HttpResponseMessage res = await awaitable;
                Console.WriteLine("After GetTwitter GetAsync");
                dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                Console.WriteLine("After GetTwitter ReadAsStringAsync");
                foreach (var r in data.results)
                {
                    l.Add(new MyObject() { name = r.text, Origin = "Twitter" });
                }
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List<MyObject>();
            }
        }

        static async Task<List<MyObject>> GetSleep(CancellationTokenSource cts)
        {
            try
            {
                Console.WriteLine("Start GetSleep");
                List<MyObject> l = new List<MyObject>();
                await Task.Delay(5000,cts.Token);
                l.Add(new MyObject() { name = "Slept well", Origin = "sleep" });
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List<MyObject>();
            }

        } 

        static async Task<List<MyObject>> GetxSleep(CancellationTokenSource cts)
        {
            Console.WriteLine("Start GetxSleep");
            List<MyObject> l = new List<MyObject>();
            await Task.Delay(2000);
            cts.Cancel();
            l.Add(new MyObject() { name = "Slept short", Origin = "xsleep" });
            return l;
        } 

    }
}

私の説明は私のブログ投稿にあります: http://blog.bekijkhet.com/2012/03/c-async-examples-whenall-whenany.html

0
broersa

次のコードを使用できます。

        var timeoutTime = 10;

        var tasksResult = await Task.WhenAll(
                                listOfTasks.Select(x => Task.WhenAny(
                                    x, Task.Delay(TimeSpan.FromMinutes(timeoutTime)))
                                )
                            );


        var succeededtasksResponses = tasksResult
                                               .OfType<Task<MyResult>>()
                                               .Select(task => task.Result);

        if (succeededtasksResponses.Count() != listOfTasks.Count())
        {
            // Not all tasks were completed
            // Throw error or do whatever you want
        }

        //You can use the succeededtasksResponses that contains the list of successful responses

使い方:

timeoutTime変数に、すべてのタスクが完了するまでの時間制限を設定する必要があります。したがって、基本的にすべてのタスクは、timeoutTimeで設定した時間を最大で待機します。すべてのタスクが結果を返す場合、タイムアウトは発生せず、tasksResultが設定されます。

その後、完了したタスクのみを取得します。完了しなかったタスクには結果がありません。

0
Jorge Freitas

http://tutorials.csharp-online.net/Task_Combinators で提案されているカスタムタスクコンビネーターを確認してください。

async static Task<TResult> WithTimeout<TResult> 
   (this Task<TResult> task, TimeSpan timeout)
 {
   Task winner = await (Task.WhenAny 
      (task, Task.Delay (timeout)));
   if (winner != task) throw new TimeoutException();
   return await task; // Unwrap result/re-throw
}

まだ試していません。

0
Maxim Eliseev