私はしばらくの間、.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)
は完了するまで実行されないため、このコードは機能しません。これはSleep
がasync
であり、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;
}
非同期プログラミングには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;
}
}
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
}
Sleep
メソッドは非同期ですが、Thread.Sleep
は非同期ではありません。非同期の全体的な考え方は、複数のスレッドを開始するのではなく、単一のスレッドを再利用することです。 Thread.Sleepの同期呼び出しの使用をブロックしているため、機能しません。
Thread.Sleep
は、あなたが実際にやりたいことを単純化したものだと思っています。実際の実装を非同期メソッドとしてコーディングできますか?
複数の同期ブロッキング呼び出しを実行する必要がある場合は、他の場所を見てください!
この点に答えるには:
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
週末になりました!
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;
}
残念ながら、メソッドに別の待機がある場合、Task.WhenAll()は役に立ちません。非同期メソッドは並列ではありません。
実際の並列実行では、新しいタスクを手動で開始する必要があります。 Task.Run()またはConfigureAwait(false)を使用します。
詳細はこちらをご覧ください: https://www.wintellect.com/tasks-are-still-not-threads-and-async-is-not-parallel/
この記事は多くのことを説明するのに役立ちました。 FAQスタイルです。
この部分では、Thread.Sleep
が同じ元のスレッドで実行される理由を説明します-最初の混乱につながります。
「async」キーワードにより、メソッドの呼び出しがThreadPoolのキューに入れられますか?新しいスレッドを作成するには?火星にロケット船を発射するには?
いいえ、いいえ。前の質問を参照してください。 「async」キーワードは、メソッド内で「await」を使用できることをコンパイラに示します。これにより、メソッドは待機ポイントで一時停止し、待機中のインスタンスが完了すると実行が非同期に再開されます。これが、「非同期」とマークされたメソッド内に「待機」がない場合にコンパイラが警告を発行する理由です。