await 异步方法,其中完成由事件处理程序发出信号

本文关键字:程序 事件处理 信号 异步方法 await | 更新日期: 2023-09-27 18:33:07

我正在使用Windows Phone 8中的联系人对象,从异步方法中调用SearchAysnc。SearchAsync 要求处理程序订阅 SearchCompleted 事件,并通过其中一个事件参数传递其结果,异步方法需要该参数来完成其工作(包括调用其他异步方法(。

您如何等待事件的异步完成,即事件模式和异步/等待模式之间的桥梁?

我能想到的唯一解决方案是使用EventWaitHandle,在等待的任务中等待它

,如下所示:
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;
}

我的实际解决方案(不完全如上所示(确实有效。尽管上面的代码并不能精确地表示已实现的解决方案(可能是一两个编译问题(,但它应该有助于表达概念并说明我问题的重点。

这让我想知道这是否是唯一的方法,或者接近等待事件处理程序执行的最佳实践方法,如果不是,那么执行此处需要的操作的"最佳实践"是什么。

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;
}

await 异步方法,其中完成由事件处理程序发出信号

TaskCompletionSource

常用的使用方式。

未测试(不知道如何在不知道您的类/方法的情况下进行测试(,

Task<string> MyMethodAsync()
{
    Contacts cons = new Contacts();
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
    cons.SearchCompleted += (sender,args) =>
    {
        tcs.TrySetResult(args.Results);
    };
    cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
    return tcs.Task;
}

要桥接 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 库中实现了类似的原语。