2012-12-20 25 views
5

私はクライアントライブラリにアクセスできないときに、データセットを非同期的に取得する正しいコードを理解しようとしています。データ。エンドポイントと日付範囲を指定し、プレイリストのリストを取得するはずです。私が今Start()コールの後に戻ってくることはありません。注:これはWinFormで実行されています。私はタスクをよりよく理解しようとしており、待ち時間やBackgroundWorkerにジャンプしたいだけではありません。私はどこかで迷子になっているのを知っています。C#およびタスク - UIスレッドのハング - 事前非同期/ Awaitキーワード

private void GoButtonClick(object sender, EventArgs e) 
    { 
     string baseUrl = "http://someserver/api"; 
     var startDateTime = this._startDateTimePicker.Value; 
     var endDateTime = this._endDateTimePicker.Value; 
     _getPlaylistsFunc = delegate() 
      { 
       var client = new PlaylistExportClient(baseUrl); 
       return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
      }; 
     var task = new Task<List<Playlist>>(_getPlaylistsFunc); 
     task.ContinueWith((t) => DisplayPlaylists(t.Result)); 
     task.Start(); 
    } 

    private void DisplayPlaylists(List<Playlist> playlists) 
    { 
     _queueDataGridView.DataSource = playlists; 
    } 

UPDATE 私は、これらの変更を行ったが、今のアプリケーションは、UIスレッドをハングしているようです。

private void GoButtonClick(object sender, EventArgs e) 
    { 
     string baseUrl = "http://someserver/api"; 
     var startDateTime = this._startDateTimePicker.Value; 
     var endDateTime = this._endDateTimePicker.Value; 
     var token = Task.Factory.CancellationToken; 

     var context = TaskScheduler.FromCurrentSynchronizationContext(); 
     Task.Factory.StartNew(() => 
      { 
       var client = new PlaylistExportClient(baseUrl); 
       _queueDataGridView.DataSource = client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 

      },token,TaskCreationOptions.None,context); 

    } 
+0

'GetPlaylistsByDateRange'にブレークポイントを置くと、実際に呼び出されることがわかりますか? – user7116

+2

他のスレッドプールスレッドではなくUIスレッドで実行されるように、継続コンテキストに同期コンテキストを渡す必要があります。それ以外は、私はここに本当の誤りは見当たりません。 – Servy

+2

アップデートでは、UIスレッドの*すべての*タスクが実行されます。第2のものだけがそこで走らなければならない。 –

答えて

2

バックグラウンドスレッドのUIコントロールのプロパティに割り当てられているようです。それはたいてい悪いニュースです。 WPFは、通常、WinFormsについてはわからない、例外をスローします。

バックグラウンドスレッドでデータをキャプチャしますが、UIコントロールに割り当てる前にメインUIスレッドに切り替えます。

var uiSync = SynchronizationContext.Current; 
    Task.Factory.StartNew(() => 
     { 
      var client = new PlaylistExportClient(baseUrl); 
      var list = client.GetPlaylistsByDateRange(...).ToList(); 
      uiSync.Post(() => _queueDataGridView.DataSource = list, null); 
     },token,TaskCreationOptions.None,context); 
+0

Hmm。私は理解する - 私は思う。 .Post()は私のコンテキスト上のメソッドではありません。 – BuddyJoe

+0

Woops。さて、あなたがコンテキスト変数を呼んでいるのは、実際はスケジューラです。 – dthorpe

+0

Post()lambda引数を試していますが、今はIDEが匿名関数の不適切な署名を報告しています。 – BuddyJoe

3

タスクベースの非同期パターンを使用することをお勧めします。これはかなり簡単です:

private async void GoButtonClick(object sender, EventArgs e) 
{ 
    string baseUrl = "http://someserver/api"; 
    var startDateTime = this._startDateTimePicker.Value; 
    var endDateTime = this._endDateTimePicker.Value; 
    var playlists = await Task.Run(() => 
    { 
     var client = new PlaylistExportClient(baseUrl); 
     return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
    }); 
    _queueDataGridView.DataSource = playlists; 
} 

これはスレッドプールスレッドをブロックすることに注意してください。ライブラリをGetPlaylistsByDateRangeAsyncメソッドに変更することができれば、これをより効率的にすることができます。

編集:あなたは.NET 4.0で立ち往生している場合は、あなたが完全なasync/await能力を得るためにMicrosoft.Bcl.Asyncをインストールすることができます。場合 - いくつかの不可解な理由のために - あなたはまだasync/awaitを使用することはできません、あなたはこのようにそれを行うことができます。

private void GoButtonClick(object sender, EventArgs e) 
{ 
    string baseUrl = "http://someserver/api"; 
    var startDateTime = this._startDateTimePicker.Value; 
    var endDateTime = this._endDateTimePicker.Value; 
    var context = TaskScheduler.FromCurrentSynchronizationContext(); 
    Task.Run(() => 
    { 
     var client = new PlaylistExportClient(baseUrl); 
     return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
    }).ContinueWith(t => 
    { 
     _queueDataGridView.DataSource = t.Result; 
    }, context); 
} 

はしかし、あなたのエラー処理は、このアプローチのより複雑であることに注意してください。

+0

私はこれがどのようにpre-async/awaitsを行うのかを定義しようとしていました。 – BuddyJoe

+0

質問に指定された 'async-await'タグに基づいて答えを出しました。' 'Task.ContinueWith'(' TaskScheduler.FromCurrentSynchronizationContext'と組み合わせて)または 'BackgroundWorker' 'async'コードはこれらのオプションよりもはるかにクリーンです –

+0

' ContinueWith'アプローチを使ってコードの更新された答えを見てください –

関連する問題