web-dev-qa-db-ja.com

「非同期」と「待機」を使用する方法とタイミング

私の理解していることから、 asyncawait doはコードを読みやすく、読みやすくすることですが、それらを使用してバックグラウンドスレッドを生成して長期ロジックを実行するのと同じことですか?

私は現在最も基本的な例を試しています。インラインでコメントを追加しました。私のためにそれを明確にしてもらえますか?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
830
Dan Dinu

asyncおよびawaitを使用すると、コンパイラはバックグラウンドでステートマシンを生成します。

これが私が起こっているハイレベルの詳細のいくつかを説明できることを願っている例です: 

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

それでは、ここでどうなりますか。

  1. Task<int> longRunningTask = LongRunningOperationAsync();LongRunningOperationの実行を開始します

  2. メインスレッド(Thread ID = 1)を仮定してawait longRunningTaskに到達したとします。 

    longRunningTaskが終了しておらず、まだ実行中の場合、MyMethodAsync()は呼び出し元のメソッドに戻るので、メインスレッドはブロックされません。 longRunningTaskが実行されると、ThreadPoolからのスレッド(どのスレッドでもかまいません)は以前のコンテキストでMyMethodAsync()に戻り、実行を継続します(この場合、結果をコンソールに出力します)。 

2番目のケースは、longRunningTaskがすでに実行を終了しており、結果が利用可能であることです。 await longRunningTaskに到達した時点ですでに結果が得られているので、コードはまったく同じスレッドで実行を継続します。 (この場合、コンソールに結果を印刷します)。もちろん、これはTask.Delay(1000)が関係している上記の例には当てはまりません。 

629
Dan Dinu

他の答えに加えて、 awaitを見てください(C#リファレンス)

そしてより具体的に含まれている例では、それはあなたの状況を少し説明します

次のWindowsフォームの例は、 asyncメソッドWaitAsynchronouslyAsyncでのawaitの使用方法を示しています。そのメソッドの動作をWaitSynchronouslyの動作と比較してください。タスクにawait 演算子が適用されていない場合、WaitSynchronouslyは定義内でasync修飾子を使用し、本体内で[.____。Thread.Sleep]を呼び出しているにもかかわらず、同期的に同期的に実行されます.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
142
Adriaan Stander

私の理解したところでは、非同期および待機が行う主なことの1つは、コードを読み書きしやすくすることです。

非同期 コードを読みやすく、読みやすくするためです。

バックグラウンドスレッドを生成して長期間ロジックを実行するのと同じことですか?

どういたしまして。

//このメソッドが「async」としてマークされる理由を理解していません。

asyncキーワードは、awaitキーワードを有効にします。そのため、awaitを使用するメソッドはすべてasyncとマークする必要があります。

// DoSomethingAsync()メソッドから5秒間スリープした後、この行に到達します。すぐに届くべきではないですか? 

いいえ、asyncメソッドはデフォルトで別のスレッドで実行されないためです。

//これはバックグラウンドスレッドで実行されますか?

いいえ.


私の async/awaitイントロ が役に立つでしょう。 公式のMSDNドキュメント も異常に優れており(特に _ tap _ セクション)、asyncチームはすばらしい _ faq _ を出しました。

122
Stephen Cleary

説明

これがasync/awaitの高レベルでの簡単な例です。これ以外にも考慮すべき詳細がたくさんあります。

注:Task.Delay(1000)は1秒間作業をシミュレートします。これは外部リソースからの応答を待つものと考えるのが最善だと思います。私たちのコードは応答を待っているので、システムは実行中のタスクを横にして終了したら戻ってくることができます。その間、そのスレッドで他の作業を行うことができます。

以下の例では、 最初のブロック がまさにそれをしています。それはすべてのタスクを直ちに開始し(Task.Delay行)、それらを横に置きます。次の行に進む前に1秒の遅延が完了するまで、コードはawait a行で一時停止します。 bcd、およびeはすべてaとほぼ同じタイミングで実行を開始したので(待機がないため)、この場合はほぼ同時に終了するはずです。

以下の例では、 2番目のブロック がタスクを開始し、それが終了するのを待っています(つまり、awaitが後続のタスクを開始します)。これを繰り返すたびに1秒かかります。 awaitはプログラムを一時停止し、続行する前に結果を待っています。これが1番目と2番目のブロックの主な違いです。

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

出力:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

SynchronizationContextに関する追加情報

注:これは、私にとっては少し霧が出るところです。なにか間違っていたら、訂正してください。答えを更新します。これがどのように機能するかについての基本的な理解を持つことが重要ですが、ConfigureAwait(false)を決して使わない限り、専門家にならなくても達成できます。最適化の機会を失う可能性があります。

これには、非同期/待機の概念を理解するのがやや難しいという1つの側面があります。これは、この例では、これがすべて同じスレッド(または少なくともSynchronizationContextに関して同じスレッドであると思われるもの)で発生しているという事実です。デフォルトでは、awaitは実行していた元のスレッドの同期コンテキストを復元します。たとえば、ASP.NETには、リクエストが入ったときにスレッドに結び付けられるHttpContextがあります。このコンテキストには、言語、IPアドレス、ヘッダーなどの元のRequestオブジェクトなど、元のHttpリクエストに固有のものが含まれます。あなたが何かを処理している途中でスレッドを切り替えるならば、あなたは潜在的に悲惨かもしれない別のHttpContextでこのオブジェクトから情報を引き出そうと試みることになるかもしれません。あなたが何かのために文脈を使っていないのを知っているならば、あなたはそれを「気にしない」ことを選ぶことができます。これは基本的にあなたのコードが別のスレッドで実行されることを可能にします、それにコンテキストを持ってくることなく。

どのようにこれを達成しますか?デフォルトでは、await a;コードは実際にはコンテキストをキャプチャして復元したいという想定をしています。

await a; //Same as the line below
await a.ConfigureAwait(true);

メインコードが元のコンテキストなしで新しいスレッドで続行できるようにしたい場合は、コンテキストを復元する必要がないことを知っているので、単にtrueではなくfalseを使用します。

await a.ConfigureAwait(false);

プログラムが休止された後、それは、異なるコンテキストを持つ全く異なるスレッドで 潜在的に を続けます。これはパフォーマンスの向上がもたらされるところです - それはそれが始まった元のコンテキストを復元する必要なしにどんな利用可能なスレッドでも続けることができます。

これは混乱しますか?うん、うん!あなたはそれを理解することができますか?恐らく!概念を理解したら、次に非同期/待機を技術的に理解している人に向けられる傾向があるStephen Clearyの説明に進みます。

116
Joe Phillips

上記の説明を簡単なコンソールプログラムで実行中 -

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

そして出力は次のとおりです。

Starting Long Running method...
Press any key to exit...
End Long Running method...

したがって、

  1. MainはTestAsyncAwaitMethodsを介して長時間実行メソッドを起動します。それは現在のスレッドを停止せずにすぐに戻ります。
  2. この間、LongRunningMethodはバックグラウンドで実行されています。完了すると、Threadpoolの別のスレッドがこのコンテキストを取得して最終メッセージを表示します。

したがって、スレッドはブロックされません。 

47
sppc42

System.Threading.Thread.Sleepで悪い例を選んだと思います

asyncタスクのポイントは、DownloadFileAsyncを実行するなど、メインスレッドをロックせずにバックグラウンドで実行できるようにすることです。

System.Threading.Thread.Sleepは「完了」しているものではなく、単にスリープしているため、5秒後に次の行に到達します...

この記事を読んで、asyncawaitの概念の素晴らしい説明だと思います: http://msdn.Microsoft.com/en-us/library/vstudio/hh191443.aspx

39
Vnuk

これは、フォローしている人にわかりやすくするための簡単なコンソールプログラムです。 "TaskToDo"メソッドはあなたが非同期にしたいあなたの長期実行メソッドです。 Asyncを実行するには、TestAsyncメソッドを使用します。テストループメソッドは単に "TaskToDo"タスクを実行し、それらを非同期で実行します。実行から実行までの順序が同じではなく、完了したときにコンソールUIスレッドに報告されているため、結果に表示されていることがわかります。単純ですが、単純化された例は、より複雑な例よりもパターンの中心を引き出すと思います。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
17
MarkWalls

この回答は、ASP.NET固有の情報を提供することを目的としています。

以下の記事で説明されているように、MVCコントローラで非同期/待機を利用することで、スレッドプールの利用率を高め、はるかに優れたスループットを達成することができます。

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

起動時に大量の同時要求が発生する、または負荷が急増する(同時実行性が突然増加する)Webアプリケーションでは、これらのWebサービス呼び出しを非同期にすると[.____。アプリケーションの応答性非同期要求は、同期要求と同じ処理時間がかかります。たとえば、要求が完了するのに2秒かかるWebサービス呼び出しを行う場合、その要求が同期的に実行されるか非同期的に実行されるかにかかわらず、2秒かかります。しかし、非同期呼び出しの間、スレッドは最初の要求が完了するのを待つ間、他の要求への応答からブロックされません。したがって、長時間の操作を呼び出す同時要求が多数ある場合、非同期要求は要求キューイングおよびスレッドプールの増大を防ぎます。

11
Lex Li

ここでのすべての答えは、Task.Delay()またはその他の組み込み非同期関数を使用します。しかし、ここで私の例はこれらの非同期関数のどれも使用していません:

    // Starts counting to a large numbewr and then immediately displays message "i'm counting...". 
    // Then it waits for task to finish and displays "finished, press any key".
    static void asyncTest ()
    {
        Console.WriteLine("Started asyncTest()");
        Task<long> task = asyncTest_count();
        Console.WriteLine("Started counting, please wait...");
        task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
        //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
        Console.WriteLine("Finished counting.");
        Console.WriteLine("Press any key to exit program.");
        Console.ReadLine();
    }

    static async Task<long> asyncTest_count()
    {
        long k = 0;
        Console.WriteLine("Started asyncTest_count()");
        await Task.Run(() =>
        {
            long countTo = 100000000;
            int prevPercentDone = -1;
            for (long i = 0; i <= countTo; i++)
            {
                int percentDone = (int)(100 * (i / (double)countTo));
                if (percentDone != prevPercentDone)
                {
                    prevPercentDone = percentDone;
                    Console.Write(percentDone.ToString() + "% ");
                }

                k = i;
            }
        });
        Console.WriteLine("");
        Console.WriteLine("Finished asyncTest_count()");
        return k;
    }
10
Tone Škoda

正直なところ、私は今でも最良の説明はウィキペディアでの将来と約束に関するものであると思います: http://en.wikipedia.org/wiki/Futures_and_promises

基本的な考え方は、タスクを非同期的に実行するスレッドの別のプールがあるということです。それを使うとき。ただし、オブジェクトはいつか操作を実行し、要求したときに結果を返すという約束をします。つまり、結果を要求して終了していない場合はブロックされますが、それ以外の場合はスレッドプールで実行されます。

そこからあなたは物事を最適化することができます:いくつかの操作は非同期で実装されることができますそしてあなたはそれ以降の要求をまとめてそして/またはそれらを並べ替えることによってfile IOとネットワークコミュニケーションのような物事を最適化できます。これが既にMicrosoftのタスクフレームワークに含まれているかどうかはわかりませんが、そうでない場合は、最初に追加することの1つになります。

あなたは実際にC#4.0のyieldで将来のパターンソートを実装することができます。それが正確にどのように機能するのか知りたいのであれば、まともな仕事をするこのリンクをお勧めすることができます: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ 。しかし、あなたが自分でそれを試してみると、クールなことをすべてやりたいのであれば、本当に言語サポートが必要であることに気づくでしょう。それがまさにマイクロソフトがしたことです。

9
atlaste

シンプルなコンソールアプリケーションの実行については、このフィドル https://dotnetfiddle.net/VhZdLU を参照してください(可能な場合は改善してください)。 これは、同じプログラム内のTask、Task.WaitAll()、asyncおよびawait演算子の使用法を示します。

このフィドルは、実行サイクルの概念をクリアするはずです。

サンプルコードはこちら

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

出力ウィンドウからのトレース:enter image description here

7
vibs2006

最速の学習のため.

  • メソッドの実行フローを理解する(図を使用):3分

  • 質問の内観(日本酒を学ぶ):1分

  • 構文の砂糖をすばやく取得:5分

  • 開発者の混乱を共有する:5分

  • 問題:通常のコードの実際の実装を非同期コードにすばやく変更する:2分

  • 次はどこ?

メソッド実行フローを理解する(ダイアグラム付き):3分

この画像では、#6に注目してください enter image description here

#6ステップ:AccessTheWebAsync()は、getStringTaskの結果なしで実行できる作業が不足しています。したがって、AccessTheWebAsyncはawait演算子を使用して進行を一時停止し、呼び出し元に制御を戻します(yield)。 AccessTheWebAsyncは、呼び出し元にタスク(文字列の戻り値)を返します。タスクは、文字列の結果を生成する約束を表します。 しかし、いつコールを返しますか? 2回目の呼び出しですか?

AccessTheWebAsync()の呼び出し元は、waitだけを行いました(内部タスクを実行してから、必要に応じて待機できます)。そのため、呼び出し側はAccessTheWebAsyncを待機しており、AccessTheWebAsyncは現在GetStringAsyncを待機しています。

メソッドはすでに返されていることを忘れないでください。再度返すことはできません(2回目は不可)。それでは、発信者はどのように知るのでしょうか?Tasks!がすべて返されました。タスクが待機された(メソッドではなく、値ではない)。タスクに値が設定されます。タスクのステータスは完了に設定されます。発信者はタスクを監視するだけです。後で here をさらに読みます。

日本酒を学ぶための質問の内観:1分

質問を少し調整しましょう。

使用方法とタイミング asyncおよびawait Tasks

Taskの学習は自動的に他の2をカバーするためです。少なくとも学習のために。もちろん、これはasyncおよびawaitに関する質問への回答です。

構文シュガーをすばやく取得:5分

  • 変換前(元の方法)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • 上記のメソッドを呼び出す別のTask-ifiedメソッド

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

待機または非同期に言及しましたか?いいえ。上記のメソッドを呼び出して、タスクを取得します。これを監視できます。タスクが返すものは既にわかっています。整数です。

  • タスクの呼び出しはやや注意が必要です。 MethodTask()を呼び出しましょう

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

タスクが完了するのを「待っています」。したがって、await。 awaitを使用するため、async(必須)とMethodAsyncをプレフィックスとして 'Async'(コーディング標準)と共に使用する必要があります。後で here

開発者の混乱を共有する:5分

開発者がTaskを実装しないというミスを犯しましたが、それでも動作します!質問と受け入れられた回答 ここで提供されたもの を理解するようにしてください。あなたが読んで完全に理解したことを願っています。同様に、すでに構築されているMethodAsync()を呼び出す例では、自分でTaskMethodTask())を使用してそのメソッドを実装するよりも簡単です。ほとんどの開発者は、コードを非同期コードに変換する際にTasksを回避するのが難しいと感じています。

ヒント:既存の非同期実装(MethodAsyncToListAsyncなど)を見つけて、問題を外部委託してください。そのため、Asyncとawaitを処理するだけです(これは簡単で、通常のコードとかなり似ています)

問題:通常のコードの実際の実装を非同期操作にすばやく変更する:2分

以下のデータレイヤーのコード行が壊れ始めました(多くの場所)。コードの一部を.Net framework 4.2から.Net coreに更新したためです。これをアプリケーション全体で1時間で修正する必要がありました!

var myContract = query.Where(c => c.ContractID == _contractID).First();

やさしい!

  1. EntityFrameWork nuget(QueryableExtensionsがあります)
  2. 名前空間= Microsoft.EntityFrameworkCore

コードはこのように変更されました

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. メソッドのシグネチャが変更されました

    Contract GetContract(int contractnumber)

    async Task<Contract> GetContractAsync(int contractnumber)

  2. 呼び出しメソッドも影響を受けました:GetContractAsync(123456);GetContractAsync(123456).Result;として呼び出されました

  3. 30分でどこでも変更しました!

しかし、アーキテクトは、これだけのためにEntityFrameWorkライブラリを使用しないように言っています!おっとっと!ドラマ!次に、カスタムタスクの実装を作成しました。あなたはどのように知っています。まだ簡単!

Where to Next?ASP.Net Core で非同期呼び出しを非同期に変換することについての素晴らしいクイックビデオがあります。それはおそらくこれを読んだ後に進む方向だからです。

5
Blue Clouds
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

より高いレベルでは:

1)Asyncキーワードは待機を有効にし、それがすべてです。 Asyncキーワードは別のスレッドでメソッドを実行しません。最初の非同期メソッドは、時間のかかるタスクで待機するまで同期的に実行されます。

2)T型のTaskまたはTaskを返すメソッドを待つことができます。async voidメソッドを待つことはできません。

3)メインスレッドが時間のかかるタスクを待っているとき、または実際の作業が開始されたとき、メインスレッドは現在のメソッドの呼び出し元に戻ります。 

4)メインスレッドがまだ実行中のタスクを待っているのを見た場合、それを待たずに現在のメソッドの呼び出し元に戻ります。このようにして、アプリケーションは応答性を保ちます。

5)処理タスクを待ち、スレッドプールとは別のスレッドで実行されます。

6)この待機タスクが完了すると、その下のすべてのコードが別のスレッドによって実行されます。

以下はサンプルコードです。実行してスレッドIDを確認してください。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
3
ABajpai

私が理解しているように、ミックスに3番目の用語Taskを追加する必要があります。

Asyncは、メソッドが非同期メソッドであると言うためにメソッドに付ける単なる修飾子です。

Taskは、async関数の戻り値です。非同期に実行されます。

あなたはawaitタスクです。コードの実行がこの行に達すると、制御は元の関数の呼び出し元に戻ります。

代わりに、async関数の戻り値(つまりTask)を変数に割り当てた場合、コードの実行がこの行に達すると、周囲のその行をcontinues function whileTaskは非同期に実行されます。

2
user21306

次のコードでは、HttpClientメソッドGetByteArrayAsyncがタスクgetContentsTaskを返します。タスクは、タスクが完了したときに実際のバイト配列を生成するという約束です。 getContentsTaskが完了するまで、SumPageSizesAsync内で実行を中断するために、待機オペレーターがgetContentsTaskに適用されます。その間、制御はSumPageSizesAsyncの呼び出し元に返されます。 getContentsTaskが終了すると、await式はバイト配列に評価されます。

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
1
lazydeveloper

非同期/待機

実際、Async/Awaitは、非同期タスクのコールバックを作成するための単なる構文上の砂糖であるキーワードのペアです。

例としてこの操作を取ります:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

この方法にはいくつかの欠点があります。エラーは伝えられず、読むのは非常に困難です。しかしAsyncとAwaitが助けになってくれます。

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

待機呼び出しは非同期メソッド内になければなりません。これにはいくつかの利点があります。

  • タスクの結果を返します
  • 自動的にコールバックを作成します
  • エラーをチェックし、それらをcallstack内でバブルアップさせます(callstack内で待っていない呼び出しまでのみ)
  • 結果を待つ
  • メインスレッドを解放する
  • メインスレッドでコールバックを実行します
  • タスクにスレッドプールのワーカースレッドを使用します。
  • コードを読みやすくする
  • そしてもっとたくさん

_ note _ :AsyncとAwaitはwith非同期呼び出しnotを使うために使われます。 Task.Run()のように、これには Task Libary を使用する必要があります。

これがawaitとnone awaitのソリューションの比較です。

これは非同期ではない解決策です。

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        Thread.Sleep(1000);
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

これは非同期メソッドです。

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

実際には、awaitキーワードを指定せずにasyncメソッドを呼び出すことができますが、これはリリースモードではここでのExceptionが飲み込まれることを意味します。

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

AsyncとAwaitは、並列コンピューティング用ではありません。メインスレッドをブロックしないために使用されます。それはasp.netまたはWindowsアプリケーションに関するものであれば。ネットワーク呼び出しによってメインスレッドをブロックするのは悪いことです。これを行うと、アプリが応答しなくなるかクラッシュする可能性があります。

ms docs をチェックして、例をいくつか入手してください。

1
Hakim

は長い期間のロジックを実行するためにバックグラウンドスレッドを生成するのと同じようにそれらを使用していますか?

この記事 MDSN:asyncとawaitを使った非同期プログラミング(C#) それを明示的に説明します。

Asyncキーワードとawaitキーワードによって追加のスレッドが作成されることはありません。 async メソッドは独自のスレッドでは実行されないため、asyncメソッドはマルチスレッドを必要としません。このメソッドは現在の同期コンテキストで実行され、メソッドがアクティブな場合にのみスレッドの時間を使用します。

1
Dmitry G.

以下は、promiseを使用するタイミングとasyncを使用するタイミングを決定するために使用する経験則です。

  1. 非同期関数は約束を返します。その逆もまた真実です。 promiseを返すすべての関数は、async function awaitがasync関数の呼び出しに使用され、それが解決または拒否されるのを待つと見なすことができます。

  2. awaitは、それが配置されているasync関数内のコードの実行をブロックします。 function2の出力がfunction1の出力に依存している場合は、awaitを使用します。

  3. 2つの関数を並列に実行できる場合は、2つの異なる非同期関数を作成してからそれらを並列に実行します。

  4. Promiseを並行して実行するには、promiseの配列を作成してからPromise.all(promisesArray)を使用します。使用するたびに、ブロッキングコードを作成していることを忘れないでください。時間が経つにつれて我々はこれを無視する傾向がある。

  5. その中にasyncFunction()を待っている巨大な非同期関数を作成する代わりに、もっと小さな非同期関数を作成するのが良いでしょう。このようにして、私たちはあまりにも多くのブロックコードを書いていないことに気づくでしょう。より小さな非同期関数を使用することのもう1つの利点は、並列実行できる非同期関数は何かを自分で考えさせることです。

  6. コードにブロッキングコードが含まれている場合は、それを非同期関数にすることをお勧めします。こうすることによって、あなたは他の誰かがあなたの関数を非同期的に使えることを確実にしています。

  7. ブロッキングコードから非同期関数を作ることによって、あなたはあなたの関数を呼び出すユーザーが彼が望む非同期性のレベルを決定することを可能にしています。

いつこれを使用するのか、いつ使用するのか、いつ使用するのかを簡単に決定できることを願っています。

0
Sunny Sultan

以下はダイアログを開いてExcelファイルを読み込み、asyncを使ってExcelから1行ずつ読み込んでgridにバインドするコードを非同期で実行するコード 

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill Excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
0
Zaheer Abbas

非同期で簡単な説明を待つ

単純な類推

朝の列車のために人は 待つ することができる。これが彼らが現在実行している彼らの主要な仕事であるので、これは彼らがしているすべてです。 (同期プログラミング(あなたが普段やっていること!))

他の人は 待つ 彼らがたばこを吸っている間彼らの朝の電車でコーヒーを飲む。 (非同期プログラミング)

非同期プログラミングとは

非同期プログラミングとは、プログラマーが自分のコードの一部を実行のメインスレッドとは別のスレッドで実行してから、その完了をメインスレッドに通知することです。

asyncキーワードは実際に何をしますか?

Asyncキーワードのようなメソッド名の接頭辞 

async void DoSomething(){ . . .

プログラマが非同期タスクを呼び出すときにawaitキーワードを使用できるようにします。それだけです。

なぜこれが重要なのでしょうか。

多くのソフトウェアシステムでは、メインスレッドは特にユーザインタフェースに関する操作のために予約されています。私のコンピューターで完了するのに5秒かかる非常に複雑な再帰的アルゴリズムを実行しているがメインスレッド(UIスレッド)でこれを実行している場合ユーザーがアプリケーション上で何かをクリックしようとするとフリーズしているように見えるメインスレッドがキューに入っていて、現在処理中の処理が多すぎるため。その結果、メインスレッドはマウスクリックを処理してボタンクリックからメソッドを実行することはできません。

非同期と待機をいつ使用しますか。

ユーザーインターフェイスを必要としないことをしているときは、非同期キーワードを使用するのが理想的です。 

ユーザーが自分の携帯電話でスケッチできるようにするプログラムを書いているとしましょう。しかし、5秒ごとにインターネット上の天気をチェックします。

アプリケーションのユーザーがモバイルのタッチスクリーンと対話してきれいな絵を描く必要があるため、天気予報を確認するには、ポーリングが5秒ごとにネットワークに発信するのを待ちます。

AsyncとAwaitの使い方

上記の例から続けて、これを書く方法のいくつかの疑似コードがあります:

     //ASYNCHRONOUS
    //this is called every 5 seconds
    async void CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }
0
James Mallon

ここでの答えは、待機/非同期に関する一般的なガイダンスとして役立ちます。また、待機/非同期の接続方法に関する詳細も含まれています。このデザインパターンを使用する前に知っておくべき実際的な経験をあなたと共有したいと思います。

「待つ」という用語はリテラルです。したがって、あなたがそれを呼び出すスレッドは、続行する前にメソッドの結果を待ちます。 foreground スレッドでは、これはdisasterです。フォアグラウンドスレッドは、ビュー、ビューモデル、初期アニメーションなど、これらの要素でブートストラップされたものを含め、アプリを構築するという負担をかけます。だから、あなたがフォアグラウンドスレッドを待つとき、あなたはstopアプリになります。ユーザーは待機し、何も起こらないように見えるまで待機します。これは否定的なユーザーエクスペリエンスを提供します。

あなたは確かに様々な方法でバックグラウンドスレッドを待つことができます:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

これらの発言の完全なコードは https://github.com/marcusts/xamarin-forms-annoyances にあります。 AwaitAsyncAntipattern.slnという解決策を参照してください。

GitHubサイトには、このトピックに関するより詳細な議論へのリンクもあります。

0
Stephen Marcus