2つの非同期タスクを並行して実行し、.NET 4.5で結果を収集する


116

私はしばらくの間、.NET 4.5で簡単に操作できると思ったものを取得しようとしています

2つの実行時間の長いタスクを同時に起動
し、最良のC#4.5(RTM)で結果を収集したい

以下は動作しますが、私はそれが好きではありません:

  • Sleep非同期メソッドになりたいので、await他のメソッドにできます
  • それはちょうど不器用に見えます Task.Run()
  • 新しい言語機能を使用しているとはまったく思いません!

作業コード:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

機能しないコード:

更新:これは実際に機能し、正しい方法です。唯一の問題は Thread.Sleep

このコードは機能しません。Sleep(5000)タスクをすぐに開始するための呼び出しが実行中なので、Sleep(1000)完了するまで実行されません。これは事実ですSleepasync、私はすぐに使用しawaitたり、呼び出したりしていません.Result

メソッドをTask<T>呼び出して実行しないようにする方法があるのではないかと思ったasyncのでStart()、2つのタスクを呼び出すことができましたがTask<T>、非同期メソッドの呼び出しからを取得する方法がわかりません。

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

注:Goを非同期メソッドにしても違いはありません
Simon_Weaver

3
awaitキーワードなしのSleepメソッドは同期task1.Resultしているvar task1 = Sleep(5000)ため、ブロックはnot atで発生しています。
Arvisは

回答:


86

非同期プログラミングでは、Sleepの代わりにTask.Delayを使用し、次にTask.WhenAllを使用してタスクの結果を組み合わせる必要があります。タスクは並行して実行されます。

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

11
これは素晴らしい答えです...しかし、私がそれを実行するまで、この間違った答えであると思いました。その後、私は理解しました。実際には5秒で実行されます。コツは、タスクをすぐに待たずに、代わりにTask.WhenAllを待つことです。
Tim Lovell-Smith

113
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

2
あなたがt1、t2をタスクとして宣言するので、私は+1します。これは正しい方法です。
ミニーム2013

12
このソリューションでは、Goメソッドも非同期である必要があると思います。つまり、非同期になる機能を公開します。呼び出し側のGoメソッドが同期しているが、2つの独立したタスクを非同期に完了したい(つまり、どちらも先に完了する必要はないが、実行を続行する前に両方とも完了している必要がある)場合のような、askersのケースのようなものが必要な場合は、Task.WaitAllより良いでしょう。 awaitキーワードは必要ないため、呼び出しGoメソッド自体を非同期にする必要はありません。 どちらのアプローチも優れていません。それは単にあなたの目標が何であるかという問題です。
AaronLS 2013

1
Voidメソッド:async void LongTask1() {...}Task.Resultプロパティがありません。このような場合は、TなしでTaskを使用してくださいasync Task LongTask1()
Arvis

どちらのタスクでも結果が得られませんでした。だから私はそれを変更しTask<TResult> t1 = LongTask1();、今私は得るt1.Result<TResult>結果の戻りタイプです。return <TResult>これを機能させるには、メソッドにが必要です。
gilu

1
本当に簡単なことをしていて、追加の変数t1t2変数が必要ない場合は、を使用できることに言及する価値があるかもしれませんnew Task(...)。例:int int1 = 0; await Task.WhenAll(new Task(async () => { int1 = await LongTask1(); }));。このアプローチの1つのキャッチは、変数が割り当てられたことをコンパイラが認識せず、初期値を指定しないと、変数が割り当てられていないものとして扱われることです。
Robert Dennis

3

あなたの中Sleepの方法は、非同期である、Thread.Sleepではありません。非同期の全体的な考え方は、単一のスレッドを再利用することであり、複数のスレッドを開始することではありません。Thread.Sleepへの同期呼び出しを使用してブロックしたため、機能しません。

これはThread.Sleepあなたが実際にやりたいことを単純化したものだと思います。実際の実装を非同期メソッドとしてコーディングできますか?

複数の同期ブロッキング呼び出しを実行する必要がある場合は、別の場所を探してください。


リチャードに感謝-はい、実際にサービスコールを使用すると、期待どおりに動作するようです
Simon_Weaver

次に、非同期を実行するにはどうすればよいですか?:とき私は「いつすべてのために、」それは最初に私が言ったにも関わらず、第二、その後、最初に実行私は、ファイルのファイルスイッチングおよび待機の多くを行うアプリケーション、約5秒、その後、別のプロセスを持ってvar x = y()、そしてないvar x=await y()か、y().wait()まだ、まだ、それはずっと待ってください、そして非同期がそれ自体でそれを処理しない場合、私は何をすべきですか?yは非同期で装飾されていることに注意してください。割り当てられた場所ではなく、すべてのときにすべてを実行することを期待しています。編集:私はパートナーに言ったとき、試してみましょうTask.Factory。このクラスの側
deadManN

2

この点に答えるには:

スリープを非同期メソッドにして、他のメソッドを待つことができるようにしたい

あなたはおそらくSleepこのように関数を書き直すことができます:

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

このコードを実行すると出力されます:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

2

それはです週末になりました!

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }

0

この記事は多くのことを説明するのに役立ちました。それはFAQスタイルです。

非同期/待機に関するFAQ

この部分では、なぜThread.Sleep同じ元のスレッドで実行するのかを説明します-私の最初の混乱につながります。

「async」キーワードにより、メソッドの呼び出しによってThreadPoolにキューイングされますか?新しいスレッドを作成するには?火星にロケット船を発射するには?

いいえ、ありません。前の質問を参照してください。「async」キーワードは、メソッド内で「await」を使用できることをコンパイラに示します。これにより、メソッドは待機ポイントで一時停止し、待機したインスタンスが完了すると非同期に実行が再開されます。これが、「非同期」とマークされたメソッド内に「待機」がない場合にコンパイラが警告を発行する理由です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.