我在整个代码中使用async/await模式.但是,有一个API使用基于事件的异步模式.我已经阅读了MSDN和几个StackOverflow的答案,这样做的方法是使用TaskCompletionSource.
我的代码:
public static TaskProcess(Stream data) { var client = new ServiceClient(); var tcs = new TaskCompletionSource (); client.OnResult += (sender, e) => { tcs.SetResult(e.Result); }; client.OnError += (sender, e) => { tcs.SetException(new Exception(e.ErrorMessage)); }; client.Send(data); return tcs.Task; }
并称为:
string result = await Process(data);
或者,用于测试:
string result = Process(data).Result;
该方法总是很快返回,但是没有一个事件被触发.
如果我添加tcs.Task.Await(); 就在return语句之前,它可以工作,但是这并不能提供我想要的异步行为.
我已经比较了我在互联网上看到的各种样本,但没有看到任何差异.
问题在于,在您的Process
方法终止后,您的ServiceClient
本地变量有资格进行垃圾收集,并且可能在事件触发之前收集,因此存在竞争条件.
为了避免这种情况,我将在类型上定义ProcessAsync
为扩展方法:
public static class ServiceClientExtensions { public static TaskProcessAsync(this ServiceClient client, Stream data) { var tcs = new TaskCompletionSource (); EventHandler resultHandler = null; resultHandler = (sender, e) => { client.OnResult -= resultHandler; tcs.SetResult(e.Result); } EventHandler errorHandler = null; errorHandler = (sender, e) => { client.OnError -= errorHandler; tcs.SetException(new Exception(e.ErrorMessage)); }; client.OnResult += resultHandler; client.OnError += errorHandler; client.Send(data); return tcs.Task; } }
并像这样消耗它:
public async Task ProcessAsync() { var client = new ServiceClient(); string result = await client.ProcessAsync(stream); }
编辑:
@usr指出通常情况下,IO操作应该是保持对其调用活动的引用的操作,这不是我们在这里看到的情况.我同意他的观点,这种行为有点奇怪,应该可能表明对象的某种设计/实现问题ServiceClient
.如果可能的话,我建议查看实现,看看是否有任何可能导致引用不能保持根深蒂固的内容.