読者です 読者をやめる 読者になる 読者になる

がりらぼ

WindowsRuntimeの応援ブログ

WindowsRuntimeのIAsyncInfo非同期メソッドサンプル

WIndowsRuntimeの非同期メソッド

非同期メソッドといえば.NetのTaskがわかりやすいのでストアアプリでもよく使うけどそういえばWindowsRuntimeには特有の非同期メソッド用のインターフェースがある。

それがこの4つ

f:id:garicchi:20150509194332p:plain

  • IAsyncAction
  • IAsyncActionWithProgress
  • IAsyncOperation
  • IAsyncOperationWithProgress<TProgress,TResult>

4つすべてがIAsyncInfoというインターフェースを継承している。
IAsyncInfoは非同期処理をキャンセルすることをサポートしている。

よって、4つの非同期メソッド用のインターフェースはすべてキャンセル処理をサポートしていることになる。
個々の機能は上の画像のとおり。Actionがつくと戻り値なし、Operationがつくと戻り値あり、WithProgressで進捗サポートという感じ

非同期メソッドの作成方法

.NetのTaskで非同期メソッドを作る場合、Task.Runメソッドを使って非同期処理を作る。

private Task StartTaskAsync()
{
    return Task.Run(() =>
    {
        //すごく重い処理
    });
}

ではIAsyncではどうするのかというとAsyncInfo.Runメソッドを使う。
内部でTaskを内包する形になるのでイメージとしては.NetのTaskにキャンセルや進捗報告をサポートした奴という認識のほうがいいかもしれない。

private IAsyncAction StartAsyncActionAsync()
{
    return AsyncInfo.Run((token) =>
    {
        return Task.Run(() =>
        {
            //すごく重い処理
        });
    });
}

実行時はSystem.WindowsRuntimeSystemExtensionsクラスにあるGetAwaiterメソッドとやらのおかげで非同期メソッドとしてTaskと同じようにawaitすることができる。

await StartAsyncActionAsync();

キャンセル方法

IAsync非同期メソッドをキャンセルさせるにはキャンセルリクエストを受け取って自分でキャンセル処理を書く必要がある

たとえばこんなHttpClientで20回google先生をダウンロードする非同期メソッドの場合
キャンセルリクエストが発行されていることを確認したならreturn非同期メソッドを中断する。

private IAsyncActionWithProgress<int> StartAsyncActionWithProgress()
{
    return AsyncInfo.Run<int>((token,progress) =>
    {
        return Task.Run(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                //キャンセルリクエストが発行されているなら
                if (token.IsCancellationRequested)
                {
                    //終了
                    return;
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                    //進捗報告await
                    progress.Report(i);
                }
            }
            
        });
    });
}

キャンセルしたい場合は実行中にIAsyncInfoのCancelメソッドを呼べばいい

var asyncTask = StartAsyncActionAsync();

//キャンセル
asyncTask.Cancel();

await asyncTask;

進捗報告方法

WithProgress系の非同期メソッドを作ったなら進捗報告ができる(する必要がある)

AsyncInfo.RunのTには進捗報告をする値の型を指定する するとIProgressがもらえるのでReportメソッドを実行すると進捗報告ができる

private IAsyncActionWithProgress<int> StartAsyncActionWithProgress()
{
    return AsyncInfo.Run<int>((token,progress) =>
    {
        return Task.Run(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                    //進捗報告
                    progress.Report(i);
                }
            }
            
        });
    });
}

進捗報告を確認するには呼び出し側でAsyncOperationProgressHandlerを用意する

var asyncTask = StartAsyncOperationWithProgress();
asyncTask.Progress = new AsyncOperationProgressHandler<string,int>((asyncInfo, progress) =>
{
    //Progress.Reportが呼ばれた時
    Debug.WriteLine(string.Format("進捗{0}%です!",progress));
});

var result = await asyncTask;

非同期メソッドサンプル

ということでWindowsRuntimeの4つの非同期メソッドを作ってみた例。
使いたいときはTask.Run内をテキトーに変更すればOKなはず。

/// 返却値なし、進捗報告なし非同期メソッド(IAsyncAction)
private IAsyncAction StartAsyncActionAsync()
{
    return AsyncInfo.Run((token) =>
    {
        return Task.Run(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                }
            }
            
        });
    });
}

/// 返却値なし、進捗報告あり非同期メソッド(IAsyncActionWithProgress)
private IAsyncActionWithProgress<int> StartAsyncActionWithProgress()
{
    return AsyncInfo.Run<int>((token,progress) =>
    {
        return Task.Run(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                    //進捗報告
                    progress.Report(i);
                }
            }
            
        });
    });
}

/// 返却値あり、進捗報告なし非同期メソッド(IAsyncOperation)
private IAsyncOperation<string> StartAsyncOperationAsync()
{
    return AsyncInfo.Run<string>((token) =>
    {
        return Task.Run<string>(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                if (token.IsCancellationRequested)
                {
                    return "キャンセルされました";
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                }
            }
            return "返却値";
        });
    });
}

/// 返却値あり、進捗報告あり非同期メソッド(IAsyncOperationWithProgress)
private IAsyncOperationWithProgress<string,int> StartAsyncOperationWithProgress()
{
    return AsyncInfo.Run<string,int>((token, progress) =>
    {
        return Task.Run<string>(() =>
        {
            //すごく重い処理
            for (int i = 0; i < 20; i++)
            {
                if (token.IsCancellationRequested)
                {
                    return "キャンセルされました";
                }
                else
                {
                    var client = new HttpClient();
                    client.GetStringAsync("http://google.co.jp").Wait();
                    //進捗報告
                    progress.Report(i);
                }
            }
            return "返却値";
        });
    });
}

サンプルアプリ

ついでにサンプルアプリもつくっといた

f:id:garicchi:20150509200607p:plain

IAsyncSample.zip