4

Windows Phone 8 の Contacts オブジェクトを操作して、非同期メソッド内から SearchAysnc を呼び出しています。SearchAsync では、ハンドラーを SearchCompleted イベントにサブスクライブする必要があり、イベント引数の 1 つを介してその結果を配信します。これは、非同期メソッドがそのジョブを実行するために必要です (これには、他の非同期メソッドの呼び出しが含まれます)。

イベントの非同期完了、つまりイベント パターンと async/await パターンの間のブリッジをどのように待ちますか?

私が思いついた唯一の解決策は、EventWaitHandle を使用し、次のような待機中の Task 内でそれを待機することでした。

using System.Threading;


async Task<string> MyMethod()
{
    string result = null;
    Contacts cons = new Contacts();
    EventWaitHandle handle = new EventWaitHandle(false,EventResetMode.ManualReset);
    cons.SearchCompleted += (sender,args) =>
    {
        // I do all my work on the results of the contact search in this handler
        result = SomeOtherSynchronousOperation(args.Results);

        // When I'm done, I release the thread which was waiting for the results
        handle.Set();
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");

    // I can't block this thread (or can I?)
    // So, I launch a task whose sole job is to wait
    await Task.Run(()=>
    {
       // This gets released by the Contacts.SearchCompleted handler when its work is finished,
       // so that MyMethod can finish up and deliver its result
       handle.WaitOne();
    }

    await DoOtherStuffWithResult(result);

    return result;
}

私の実際の解決策(上記のとおりではありません)は機能します。上記のコードは実装されたソリューションを正確に表しているわけではありませんが (おそらくコンパイルの問題が 1 つまたは 2 つ)、概念を表現し、私の質問のポイントを説明するのに役立つはずです。

これが唯一の方法なのか、それともイベント ハンドラーの実行を待機するためのベスト プラクティスに近い方法なのか、そうでない場合は、ここで必要なことを行うための "ベスト プラクティス" は何かという疑問が残ります。

Windows 同期プリミティブは、非同期/待機の世界でまだ使用されていますか?

(提供された回答に基づく)

これは正しいでしょうか?

using Microsoft.Phone.UserData;

string ExtractWhatIWantFromResults(IEnumerable<Contact> results)
{
    string result;

    // Do my processing on the list of contacts, stuff the results into result

    return string;
}

async Task<string> MyMethod()
{
    Contacts cons = new Contacts();
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

    cons.SearchCompleted += (sender,args) =>
    {
        tcs.TrySetResult(ExtractWhatIWantFromResults(args.Results));
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");

    return tcs.Task;
}
4

2 に答える 2

3

EAP と TAP をブリッジするには、次のように を使用TaskCompletionSourceする必要があります。

public static Task<IEnumerable<Contact>> SearchTaskAsync(this Contacts contacts, string filter, FilterKind filterKind)
{
  var tcs = new TaskCompletionSource<IEnumerable<Contact>>();
  EventHandler<ContactsSearchEventArgs> subscription = null;
  subscription = (_, e) =>
  {
    contacts.SearchCompleted -= subscription;
    tcs.TrySetResult(e.Results);
  };
  contacts.SearchCompleted += subscription;
  contacts.SearchAsync(filter, filterKind, null);
  return tcs.Task;
}

次のように使用できます。

async Task<string> MyMethodAsync()
{
  Contacts cons = new Contacts();
  var searchResults = await cons.SearchTaskAsync(String.Empty, FilterKind.None);
  string result = SomeOtherSynchronousOperation(searchResults);
  await DoOtherStuffWithResult(result);
  return result;
}

実際、TAP に関する MSDN ドキュメントは非常に質が高く、そのセクション全体を読むことを強くお勧めします。

Windows 同期プリミティブは、非同期/待機の世界でまだ使用されていますか?

スレッドをブロックするとすぐに、非同期コードの利点が失われるためです。とはいえ、TAP ベースのプリミティブを使用して同様の動作をエミュレートできます。Stephen Toub はこれを調査する一連のブログ エントリを持っており、私はAsyncEx ライブラリに同様のプリミティブを実装しました。

于 2013-09-28T23:12:24.307 に答える