非同期サポートの概要
C# 5 では、非同期プログラミングを簡略化するための 2 つのキーワード (async と await) が導入されました。 これらのキーワードを使用すると、簡単なコードを記述するだけで、タスク並列ライブラリを使用して別のスレッドで実行時間の長い操作 (ネットワーク アクセスなど) を実行し、完了時に結果に簡単にアクセスできます。 Xamarin.iOS と Xamarin.Android の最新バージョンでは、async と await がサポートされています。このドキュメントでは、Xamarin で新しい構文を使用する方法の説明と例を示します。
Xamarin での async のサポートは Mono 3.0 基盤に基づいて構築されており、API プロファイルをモバイル対応バージョンの Silverlight から .NET 4.5 のモバイル対応バージョンにアップグレードします。
概要
このドキュメントでは、新しい async キーワードと await キーワードについて説明し、Xamarin.iOS と Xamarin.Android で非同期メソッドを実装する簡単な例をいくつか紹介します。
C# 5 の新しい非同期機能の詳細 (多くのサンプルとさまざまな使用シナリオを含む) については、非同期プログラミングに関する記事を参照してください。
サンプル アプリケーションは、(メイン スレッドをブロックせずに) 単純な非同期 Web 要求を行い、ダウンロードした html と文字数で UI を更新します。
Xamarin での async のサポートは Mono 3.0 基盤に基づいて構築されており、API プロファイルをモバイル対応バージョンの Silverlight から .NET 4.5 のモバイル対応バージョンにアップグレードします。
要件
C# 5 の機能には、Xamarin.iOS 6.4 および Xamarin.Android 4.8 に含まれている Mono 3.0 が必要です。 Mono、Xamarin.iOS、Xamarin.Android、Xamarin.Mac をアップグレードして利用するように求められます。
async と await の使用
async
と await
は、タスク並列ライブラリと連携して動作する新しい C# 言語機能です。これらを使用して、アプリケーションのメイン スレッドをブロックすることなく、実行時間の長いタスクを実行するスレッドコードを簡単に記述できます。
async
宣言
async
キーワードは、メソッド宣言 (またはラムダまたは匿名メソッド) に配置され、非同期的に実行できる、つまり、呼び出し元のスレッドをブロックしないコードが含まれていることを示します。
async
でマークされたメソッドには、少なくとも 1 つの await 式またはステートメントが含まれているはずです。 メソッドに await
ステートメントが存在しない場合は、これは同期的に実行されます (async
修飾子がない場合と同じ)。 これにより、コンパイラの警告も発生します (エラーは発生しません)。
戻り値の型
async メソッドは、Task
、Task<TResult>
、void
のいずれかを返す必要があります。
メソッドが他の値を返さない場合は、Task
戻り値の型を指定します。
メソッドが値を返す必要がある場合は、Task<TResult>
を指定します。ここで、TResult
は返される型 (int
など) です。
void
戻り値の型は、主に必要なイベント ハンドラーに使用されます。 void を返す非同期メソッドを呼び出すコードは、結果で await
を行うことはできません。
パラメーター
非同期メソッドでは、ref
パラメータまたは out
パラメータを宣言できません。
await
await 演算子は、async としてマークされたメソッド内の Task に適用できます。 これにより、メソッドはその時点で実行を停止し、タスクが完了するまで待機します。
await を使用しても、呼び出し元のスレッドがブロックされるのでなく、制御が呼び出し元に返されます。 つまり、呼び出し元のスレッドはブロックされないため、ユーザー インターフェイス スレッドなどは、タスクの待機時にブロックされません。
タスクが完了すると、メソッドはコード内の同じポイントで実行を再開します。 これには、try-catch-finally ブロックがある場合、この try 範囲に戻ることが含まれます。 await は catch ブロックまたは finally ブロックには使用できません。
await の詳細についてご覧ください。
例外処理
async メソッド内で発生する例外は、タスクに格納され、タスクに await
が実行されたときにスローされます。 これらの例外は、try-catch ブロック内でキャッチして処理できます。
キャンセル
完了に時間がかかる非同期メソッドは、キャンセルをサポートする必要があります。 通常、キャンセルは次のように呼び出されます。
CancellationTokenSource
オブジェクトが作成されます。CancellationTokenSource.Token
インスタンスは、キャンセル可能な非同期メソッドに渡されます。- キャンセルは、
CancellationTokenSource.Cancel
メソッドを呼び出すことによって要求されます。
その後、タスクはそれ自体をキャンセルし、そのキャンセルを確認します。
取り消しの詳細については、「Fine Tuning Your Async Application (C#) (非同期アプリケーションの微調整 (C#))」を参照してください。
例
サンプル (iOS と Android の両方) をダウンロードして、モバイル アプリの実際の例 async
と await
を参照してください。 コード例については、このセクションで詳しく説明します。
async メソッドの記述
次のメソッドは、async
メソッドに await
させるタスクを使用して、メソッドをコーディングする方法を示しています。
public async Task<int> DownloadHomepage()
{
var httpClient = new HttpClient(); // Xamarin supports HttpClient!
Task<string> contentsTask = httpClient.GetStringAsync("https://visualstudio.microsoft.com/xamarin"); // async method!
// await! control returns to the caller and the task continues to run on another thread
string contents = await contentsTask;
ResultEditText.Text += "DownloadHomepage method continues after async call. . . . .\n";
// After contentTask completes, you can calculate the length of the string.
int exampleInt = contents.Length;
ResultEditText.Text += "Downloaded the html and found out the length.\n\n\n";
ResultEditText.Text += contents; // just dump the entire HTML
return exampleInt; // Task<TResult> returns an object of type TResult, in this case int
}
これらの点に注意してください。
- メソッドの宣言には、
async
キーワードが含まれています。 - 戻り値の型は
Task<int>
であり、呼び出し元のコードが、このメソッドで計算されるint
値にアクセスできるようにします。 - return ステートメントは、整数オブジェクトである
return exampleInt;
です。このメソッドがTask<int>
を返すことに、言語の改善の一環が表れています。
async メソッドの呼び出し 1
このボタン クリック イベント ハンドラーは、上記のメソッドを呼び出す Android サンプル アプリケーションにあります。
GetButton.Click += async (sender, e) => {
Task<int> sizeTask = DownloadHomepage();
ResultTextView.Text = "loading...";
ResultEditText.Text = "loading...\n";
// await! control returns to the caller
var intResult = await sizeTask;
// when the Task<int> returns, the value is available and we can display on the UI
ResultTextView.Text = "Length: " + intResult ;
// "returns" void, since it's an event handler
};
注:
- 匿名デリゲートには、async キーワード プレフィックスがあります。
- 非同期メソッド DownloadHomepage は、sizeTask 変数に格納されている Task<int> を返します。
- このコードは、sizeTask 変数を待機します。 ここが、メソッドが中断され、非同期タスクが独自のスレッドで終了するまで、呼び出し元のコードに制御が返される場所です。
- メソッドの最初の行にタスクが作成されるときに、タスクが作成されているにもかかわらず、実行は一時停止しません。 await キーワードは、実行が一時停止される場所を示します。
- 非同期タスクが完了すると、intResult が設定され、await 行から元のスレッドで実行が続行されます。
async メソッドの呼び出し 2
この例は、iOS サンプル アプリケーションでは、他の方法を示すために若干異なる書き方がされています。 この例では、匿名デリゲートを使用するのでなく、通常のイベント ハンドラーのように割り当てられる async
イベント ハンドラーを宣言します。
GetButton.TouchUpInside += HandleTouchUpInside;
このイベント ハンドラー メソッドは、次のように定義されます。
async void HandleTouchUpInside (object sender, EventArgs e)
{
ResultLabel.Text = "loading...";
ResultTextView.Text = "loading...\n";
// await! control returns to the caller
var intResult = await DownloadHomepage();
// when the Task<int> returns, the value is available and we can display on the UI
ResultLabel.Text = "Length: " + intResult ;
}
いくつかの重要な点:
- このメソッドは、
async
としてマークされていますが、void
を返します。 これは通常、イベント ハンドラーに対してのみ行われます (それ以外の場合、1 つのTask
またはTask<TResult>
を返します)。 DownloadHomepage
メソッドのawait
キーワードは、中間Task<int>
変数を使用してタスクを参照した前の例とは異なり、変数 (intResult
) に直接割り当てます。 ここが、非同期メソッドが別のスレッドで完了するまで、コントロールが呼び出し元に返される場所です。- 非同期メソッドが完了して戻ると、実行は
await
で再開されます。これは、整数の結果が返され、次に UI ウィジェットにレンダリングされることを意味します。
まとめ
async と await を使用すると、メイン スレッドをブロックすることなく、バックグラウンド スレッドで実行時間の長い操作を生成するために必要なコードが大幅に簡略化されます。 また、タスクが完了したときに、結果に簡単にアクセスできるようにします。
このドキュメントでは、Xamarin.iOS と Xamarin.Android の両方での新しい言語キーワードと例の概要について説明しました。