開発者が知っておくべき、ライブラリとしてのWindowsランタイム特集:デスクトップでもWinRT活用(4/5 ページ)

» 2013年04月26日 11時46分 公開
[荒井省三日本マイクロソフト]

非同期メソッドの完了をイベント・ハンドラで処理する方法

 次に、非同期メソッドの完了をイベント・ハンドラで処理するコードを示す(実行結果は同じである)。

static void Main(string[] args)
{
  IAsyncOperation<DeviceInformationCollection> deviceInfo;
  deviceInfo = DeviceInformation.FindAllAsync();   // …… (1)
  deviceInfo.Completed += FindAllAsyncCompleted;   // …… (2)
  while (deviceInfo.Status != AsyncStatus.Completed)   // …… (3)
  {
    System.Threading.Thread.Sleep(500);
  }
  Console.WriteLine("Dummy");   // …… (4)
  Console.ReadKey();
}

static void FindAllAsyncCompleted(IAsyncOperation<DeviceInformationCollection> asyncInfo, AsyncStatus asyncStatus)   // …… (5)
{
  DeviceInformationCollection deviceCollection;
  deviceCollection = asyncInfo.GetResults();   // …… (6)
  foreach (DeviceInformation device in deviceCollection)   // …… (7)
  {
    Console.WriteLine("Name={0}, ID={1}", device.Name, device.Id);
  }
}

Sub Main()
  Dim deviceInfo As IAsyncOperation(Of DeviceInformationCollection)
  deviceInfo = DeviceInformation.FindAllAsync()   ' …… (1)
  deviceInfo.Completed = AddressOf FindAllAsyncCompleted   ' …… (2)
  While (deviceInfo.Status <> AsyncStatus.Completed)   ' …… (3)
    System.Threading.Thread.Sleep(500)
  End While
  Console.WriteLine("Dummy")   ' …… (4)
  While (True)
   Dim l = Console.ReadLine()
   If l = "q" Then
     Exit While
   End If
   System.Threading.Thread.Sleep(500)
End While
End Sub

Sub FindAllAsyncCompleted(asyncInfo As IAsyncOperation(Of DeviceInformationCollection), asyncStatus As AsyncStatus)   ' …… (5)
  Dim deviceCollection As DeviceInformationCollection
  deviceCollection = asyncInfo.GetResults()   ' …… (6)
  For Each device As DeviceInformation In deviceCollection   ' …… (7)
    Console.WriteLine("Name={0},ID={1}", device.Name, device.Id)
  Next
End Sub

リスト4 非同期メソッドの完了をイベント・ハンドラで処理するコード(上:C#、下:VB)
このコード例では、WinRTのDeviceInformationクラスを完了イベントで使用している。
  (1)FindAllAsync非同期メソッドを使って、デバイス情報のコレクションを取得する。
  (2)deviceInfo変数にCompletedイベント・ハンドラを登録する。
  (3)非同期メソッドが完了するまでを待機させるためのループで、500ミリ秒間隔でチェックする。
  (4)イベント・ハンドラの実行を待機するためのマジック・コードを記述する。
  (5)完了イベントのイベント・ハンドラを定義する。
  (6)GetResultsメソッドで結果を取得する。
  (7)DeviceInformationコレクションを列挙して、デバイス情報をコンソールへ出力する。

 この方法のようにIAsyncOperationオブジェクトのCompletedイベントを処理するようにしても、非同期処理を同期的に扱うために余計なコード(具体的にはループによる待機)が必要になる点は同じだが、非同期メソッドの結果を取得するロジックやコードをイベント・ハンドラ内に集約できるメリットが生まれている。しかし、このコードには問題が多い。詳しくは説明しないが、これはイベントが何のスレッドで呼び出されているかという問題であり、この問題はGUIを使用するアプリでは問題となる可能性が高いのだ*7。だからこそ、次の応用編で非同期編を説明したい。

*7 この理由からサンプル・コードは、コンソール・アプリとして記述している。


非同期処理を同期的に扱うためのマジック・コードについて

 リスト4では、非同期メソッドを同期的に扱うためのマジック・コードが2箇所に存在している。特に問題なのが、完了時のイベント・ハンドラを待ち受けるためのマジック・コードである。筆者の環境では、C#版のコードはConsole.WriteLineメソッドを挟むことで正常に動作する。しかし、VB版のコードは結果を出力する前にMain関数が終了してしまうために、終了キー(=[q]キー)を押すまで待つように作成しなければならなかった。きっと、試す環境によっては、異なる結果が出るはずである。これが、非同期メソッドを簡易的な同期コードに書き起こすことの弊害である。本来であれば、もっと慎重にコードを設計すべきなのだ。ただし、本稿はデスクトップ・アプリからのWinRTの使い方を説明することを目的にしており、実用的なコードを提供することにはない。あくまでも、動作確認を目的とするコードであることを念頭に置いて読んでほしい。


Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。