イベントハンドラで System.Threading.Tasks.Task を使う
#TLで指摘いただいたので、記事を追加しています。
ここひと月ほど、スレッドについて勉強してます。昨日 MSDN フォーラムに非同期に関するスレが立ったので System.Threading.Tasks.Task を使ったコードを提示してみたんですが、こちらにも備忘録で書いときます。
Form で Task を使う場合、以下のようになります。
コントロールは UIスレッドでないと操作できないため Invoke メソッドを使ってアクセスします。イベントハンドラ内で Task.Wait を呼び出すとアプリケーションがフリーズするため、完了時の処理は ContinueWith メソッドを使ってます。
Option Explicit On Option Strict On Imports System.Threading.Tasks Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Task.Factory.StartNew( Sub() Me.Invoke(Sub() Me.Label1.Text = "実行中です。" Me.Button1.Enabled = False End Sub) System.Threading.Thread.Sleep(3000) End Sub). ContinueWith( Sub() Me.Invoke(Sub() Me.Label1.Text = "終了しました。" Me.Button1.Enabled = True End Sub) End Sub) End Sub End Class
WPF の場合。Forms と基本的に同じですが、非同期スレッドからコントロールにアクセスするには Dispatcher.Invoke メソッドを使います。また ViewModel なら Task.Wait メソッドを使えますが、WPF も Window のイベントハンドラで Task.Wait を呼び出すとフリーズするため、完了処理に ContinueWith を使ってます。
Option Explicit On Option Strict On Imports System.Threading.Tasks Class MainWindow Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click Task.Factory.StartNew( Sub() Me.Dispatcher.Invoke(Sub() Me.TextBlock1.Text = "実行中です。" Me.Button1.IsEnabled = False End Sub) System.Threading.Thread.Sleep(5000) End Sub). ContinueWith( Sub() Me.Dispatcher.Invoke(Sub() Me.TextBlock1.Text = "終了しました。" Me.Button1.IsEnabled = True End Sub) End Sub) End Sub End Class
Task のなにがいいのかってのは、スレッドプールを使うためスレッド数をハードに応じて自動調整してくれるし、System.Threading.Thread よりオーバーヘッドが少なく、待機や完了結果も取得可能、さらにキャンセルも可能で、タスク完了時に別タスクも起動でき、そのうえタスクをネストして使うことも可能という、極めて強力なクラスです。
#追記 2013/04/17 14:00
上のサンプルでは ContinueWith に渡すタスクで Dispatcher.Invoke 使ってますが、ContinueWith の第二引数に同期コンテキスト渡す方がが自然に思えると TL で指摘を頂きました。確かにこのタスクはコントロールしか操作しませんので 同期コンテキストタスクスケジューラー を渡した方が良さそうです。そこで TaskScheduler.FromCurrentSynchronizationContext を渡すよう実装してみました。
MSDN にも以下のように記述されてましたね(汗
同期コンテキストの指定
TaskScheduler.FromCurrentSynchronizationContext メソッドを使用すると、タスクが特定のスレッドで実行されるようにスケジュールできます。これは、Windows フォームや Windows Presentation Foundation などのフレームワークで役立ちます。これらのフレームワークでは、多くの場合、ユーザー インターフェイスオブジェクトへのアクセスが、その UI オブジェクトが作成されたスレッドで実行されているコードに制限されるからです。詳細については、「方法: ユーザー インターフェイス (UI) スレッドで作業をスケジュールする」を参照してください。MSDN ライブラリ - タスク スケジューラ
また Task.Factory で開始するタスクも Dispatcher.Invoke 使ってますが、こちらはタスク呼び出す前にイベント内で直接実行すりゃいいじゃん。というわけで修正したコードです。確かにこの方がよりすっきりしてますね。
Option Explicit On Option Strict On Imports System.Threading.Tasks Class MainWindow Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click Me.TextBlock1.Text = "実行中です。" Me.Button1.IsEnabled = False Task.Factory.StartNew(Sub() System.Threading.Thread.Sleep(5000)). ContinueWith( Sub() Me.TextBlock1.Text = "終了しました。" Me.Button1.IsEnabled = True End Sub, TaskScheduler.FromCurrentSynchronizationContext) End Sub End Class
ちなみに書籍ですが、並列プログラミングに関しては プログラミング .NET Framework 第三版が一番詳しいです。170頁以上も使ってスレッドの基礎から徹底的に解説してるし、Task の解説だけでも15頁も割いてます。一冊7000円もする本ですが、値段以上の価値ありますよ。
プログラミング.NET FRAMEWORK 第3版 (Microsoft Press)
- 作者: Jeffrey Richter,藤原雄介
- 出版社/メーカー: 日経BP
- 発売日: 2011/02/03
- メディア: 単行本
- 購入: 10人 クリック: 500回
- この商品を含むブログ (20件) を見る
概略だけなら.NET 開発テクノロジー入門にも書かれてます。概略とはいえ Task や Pallarel のサンプルや並列デバッガーの使い方までさくっと解説しています。
.NET開発テクノロジー入門 VISUAL STUDIO 2010対応版 (MSDNプログラミングシリーズ)
- 作者: マイクロソフト株式会社エバンジェリストチーム,新村剛史
- 出版社/メーカー: 日経BP
- 発売日: 2010/12/02
- メディア: 単行本
- 購入: 4人 クリック: 187回
- この商品を含むブログ (12件) を見る
検索すれば、ネット上にもリソースが見つかります。以下の記事が詳しい。
関連記事 : タスク並列ライブラリ (TPL)
関連記事 : 雑記 スレッド プールとタスク
関連記事 : TPL入門