web-dev-qa-db-ja.com

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

私はしばらくの間、.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をすぐに呼び出していない場合でも当てはまります。

asyncメソッドを呼び出すことで、実行されていないTask<T>を取得する方法があるので、2つのタスクでStart()を呼び出すことができると思いましたが、どうすればよいかわかりません非同期メソッドの呼び出しから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;
}
106
Simon_Weaver

非同期プログラミングには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;
        }
    }
73
softveda
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
}
106
Bart

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

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

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

3
Richard

この点に答えるには:

Sleepを非同期メソッドにして、他のメソッドを待機できるようにしたい

次のように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
asidis

週末になりました!

    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;
    }
2
Arvis

残念ながら、メソッドに別の待機がある場合、Task.WhenAll()は役に立ちません。非同期メソッドは並列ではありません。

実際の並列実行では、新しいタスクを手動で開始する必要があります。 Task.Run()またはConfigureAwait(false)を使用します。

詳細はこちらをご覧ください: https://www.wintellect.com/tasks-are-still-not-threads-and-async-is-not-parallel/

0
bside

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

非同期/待機FAQ

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

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

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

0
Simon_Weaver