我正在使用Windows Phone 8中的Contacts对象,在异步方法中调用SearchAysnc。 SearchAsync要求处理程序订阅SearchCompleted事件,并通过事件参数之一传递其结果,async方法需要执行其工作(包括调用其他异步方法)。
你如何等待事件的异步完成,即事件模式和async / await模式之间的桥接?
我能想出的唯一解决方案是使用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同步原语是否在async / await世界中占有一席之地?
(根据提供的答案)
这是正确的吗?
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;
}
答案 0 :(得分:6)
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;
}
答案 1 :(得分:3)
为了桥接EAP和TAP,你应该use 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;
}
事实上,MSDN docs for TAP实际上是非常高质量的,我强烈建议您阅读整个部分。
Windows同步原语是否在async / await世界中占有一席之地?
并非如此,因为只要您阻止某个线程,就会失去异步代码的好处。也就是说,您可以使用基于TAP的原语模拟类似的行为; Stephen Toub has a series of blog entries探索了这一点,并在我的AsyncEx library中实现了类似的原语。