2016-05-20 23 views
0

警告私はasync/awaitの完全な初心者ですので、これを完全に誤解しています!私の非同期コードが非同期で動作しないのはなぜですか?

私はこの仕組みがどのように機能するかを試しています。WPFウィンドウの表示で単純なコードを試しました。私は

public partial class MainWindow { 
    private Random _r = new Random(DateTime.Now.Millisecond); 

    public MainWindow() { 
    InitializeComponent(); 
    } 

    private async void Bleah_Click(object sender, RoutedEventArgs e) { 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    await DoStuffAsync(); 
    DoMoreStuffSync(); 
    AddToMsg("Done"); 
    } 

    private void DoSyncStuff() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoSyncStuff - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoSyncStuff - finished"); 
    } 

    private void DoMoreStuffSync() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoMoreStuffSync - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoMoreStuffSync - finished"); 
    } 

    private async Task DoStuffAsync() { 
    await Task.Run(() => { 
     int delay = _r.Next(500, 1500); 
     AddToMsg("DoStuffAsync - waiting for " + delay + "ms"); 
     Thread.Sleep(delay); 
     AddToMsg("DoStuffAsync - finished"); 
    }); 
    } 

    private void AddToMsg(string msg) { 
    Dispatcher.BeginInvoke(
     new Action(() => { LstMessages.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " - " + msg); })); 
    } 

LstMessagesがウィンドウ上のリストボックスで[...]ボタンのクリックイベントハンドラを追加し、次のようにいくつかの同期および非同期メソッドを追加しました。

ボタンをクリックすると、各遅延の長さにかかわらず、3つのメソッドが常に呼び出した順に実行されることがわかります。

私は明らかにこのようなことを誤解していますが、数時間読んでいて、コードのバリエーションをたくさん試してみると、私はそれが期待どおりに動作するようにはなりません。

私がここで間違っていたことを誰でも明確にすることができますか?

+0

非同期はそれだけで、呼び出し元のスレッドをブロックしませんあなたのコードを並列化しません。 – juharr

+1

良いリソースはhttps://msdn.microsoft.com/en-us/magazine/jj991977.aspxです。いくつかの例はあなたのケースに話すべきです – Brian

+2

あなたの非同期メソッドでは 'Thread.Sleep'を使わないでください。あなたが実際にスレッドをブロックしないように、 'Task.Delay'を使います。 – mason

答えて

1

この方法を試してみてください。非同期メソッドを開始してすぐにUIスレッドで待機するようです。

private async void Bleah_Click(object sender, RoutedEventArgs e) 
{ 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    Task t = DoStuffAsync(); 
    DoMoreStuffSync(); 
    await t; 
    AddToMsg("Done"); 
} 
+0

ありがとう、あなたとWill Rayの両方がこれを理解するのを助けました。私は答えとして両方の返答をマークすることができたらいいと思う。それが最初に来たのであなたの印をつけて、私の質問に直接答えた –

0

メインUIスレッドですべての操作を実行しているので、目に見えるものは意味があります。あなたは、それぞれのアプローチは独自の長所と短所を持っている

BackgroundWorker

Thread

ThreadPool

独自のスレッド/ BackgroundWorkerのオブジェクトを作成し、管理するいずれかの必要がある、またはのThreadPoolにメソッドを提出します他の回答はここにあります。これらのリンクに読んで例を試してみてください

+0

これらの3つのアプローチは、asyncを単に使用するのではなく、Task.Runで適切に待機するのとは対照的にかなりの短所を持っています。「スレッド」のセクションを参照してください。https://msdn.microsoft.com/en-us/library/mt674882.aspx –

+0

@ thunder2709返事をありがとう、しかし、私はすでにそれらを使用する方法を知っています。私は新しい機能を使う方法を学びたいと思っています –

1

重要なことは、async and await keywords don't cause additional threads to be created.Task.Run()は別のスレッドに作業を移すことができます)です。だからあなたのコードで実際に何が起こっていますか?

コードでは、最初にDoSyncStuff()を呼び出すと、メインスレッドが一時停止します。 DoStuffAsync()への電話は、DoSyncStuff()が完全に完了するまで実行されません。

DoStuffAsyncへのあなたの呼び出しは、それが非同期だかのようにトリガーされた - しかし、あなたは、呼び出し元関数内でのawaitキーワードを使用しているため、メインスレッド制御(Bleah_Click()呼び出し元に戻ります「DoStuffAsync()を待つ」あなたの目的のためにこれはないだろう何か面白い)。 DoStuffAsync()が完了すると、制御はBleah_Clickに戻り、DoMoreStuffSync()が実行され、メインスレッドが再び一時停止します。

質問:あなたが本当にあなたの望む結果を指定していないので、あなたが "間違っている"ことをあなたに伝えることはできません - UIスレッドの実行を一時停止し、あなたはすべてのことを正しく行いました。

2

あなたはがしなければならないすべてはあなたのコード内でawaitキーワードをドロップします。

エリックリペットでblog postを引用すると:

タスクは「待望」されるたびに、現在のメソッドの残りの部分は、タスクの継続としてサインアップし、その後、制御され、すぐに呼び出し元に戻ります。タスクが完了すると、継続が呼び出され、メソッドは以前の状態で起動します。

キーワード「await」を追加すると、「この非同期メソッドが完了したら、このメソッドの残りの部分を実行する」と効果的に言います。

値を返すメソッドでこれを理解するのが簡単かもしれません。次のプログラムはすぐに2つのメソッドを開始し、awaitはsyncメソッドを呼び出した後にasyncメソッドの結果を返します。 await行を動かして、動作の違いを見ることができます。

class Program 
{ 
    static void Main(string[] args) 
    { 
     MainAsync(); 
     Console.ReadKey(); 
    } 

    static async void MainAsync() 
    { 
     var task = GetNumberAsync(); 
     var syncNumber = GetNumber(); 
     var asyncNumber = await task; // moving this line above "GetNumber();" will make these run in order 

     Console.WriteLine(syncNumber); 
     Console.WriteLine(asyncNumber); 
    } 

    private static int GetNumber() 
    { 
     Console.WriteLine("DoSomeWork - started"); 
     Thread.Sleep(1000); 
     Console.WriteLine("DoSomeWork - finished"); 
     return 11; 
    } 

    private static async Task<int> GetNumberAsync() 
    { 
     Console.WriteLine("GetNumberAsync - started"); 
     await Task.Delay(1000); 
     Console.WriteLine("GetNumberAsync - finished"); 
     return 22; 

    } 
} 
+0

すばらしい返信。私は自分のものとクリスを答えにすることができればいいと思っています。彼は自分の質問に直接答えました。私は怒らないでください。しかし、私はおそらく最初に来たように彼を答えとしてマークします。再度、感謝します –

関連する問題