同步方法中的异步开始/结束

(我正在使用.Net 4.0)

我想从我的服务层异步调用WCF服务。 此服务层由MVC.Net控制器使用。 我已经读过,异步调用WCF服务是一种很好的做法。 所以我正在使用begin / end(apm)。 我想仔细检查一下我是否做得很丰富:

public byte[] GetSomeData() { IAsyncResult result = myServiceClient.BeginDoSomething(someInputValue, null, null); var data = _pdfCreatieService.EndCreateForPreview(result); return data; } 

我不完全确定上面的代码,因为我已经看到了类似下面代码的结构,在我的情况下看起来有点复杂和不必要:

 public byte[] GetSomeData() { var myState = new MyState(); IAsyncResult result = _myServiceClient.BeginDoSomething(someInputValue, CreateForPreviewCallback, myState); result.AsyncWaitHandle.WaitOne(); return myState.Bytes; } private void DoSomethingCallback(IAsyncResult result) { var myState = (MyState)result.AsyncState; myState.Bytes = _myServiceClient.EndDoSomething(result); } 

感谢Avner Shahar-Kashtan,Ned Stoyanov和Noseratio。 你的答案非常有见地!

实际上,您的代码将采用异步方法并同步调用它。 当您调用EndDoSomething方法时,您实际上是在阻塞线程,直到异步方法完成,这与异步调用完全相反。

当然,您的第二个代码块也会通过使用waithandle显式阻止执行来同步调用代码。

你想要做的是,不是从你的初始方法返回byte[] ,而是让你的DoSomethingCallback对字节做一些活动 – 将它们存储在一些可以被控制器检查的类成员中,引发一个事件,或者做别的。 如果您正在等待异步呼叫,那么您将无法获得任何好处。

如果您使用的是.NET 4.5或更高版本(或VS2012中的.NET 4.0,使用BCL异步包 ),您还可以使用async/await ,这是一个很好的包装器,可以让您合并调用方法并将回调方法转换为单一,更连贯的方法。

但无论您选择何种语法或库,您的第一步是了解异步编程必然会将代码的控制流分解为调用和结果回调,或者继续异步操作。

在ASP.NET MVC中,如果您的控制器也是异步的 ,那么您只能从异步调用中受益。 显然,您的代码不是这种情况,因为您在控制器的方法中阻止了WaitOne

使用.NET 4.5实现异步控制器非常简单,请查看“在ASP.NET MVC 4中使用异步方法”以获取更多信息。

使用.NET 4.0,它有点繁琐,请检查“在ASP.NET MVC中使用异步控制器” 。 您的控制器应派生自AsyncController并使用AsyncManager通知ASP.NET堆栈有关挂起的异步操作。

这是.NET 4.0的示例,适用于您的情况(未经测试)。 注意使用Task.Factory.FromAsyncTask.ContinueWith

 public static class WcfExt { public static Task DoSomethingAsync( this IMyService service, string someInputValue) { return Task.Factory.FromAsync( (asyncCallback, asyncState) => service.BeginDoSomething(someInputValue, asyncCallback, asyncState), (asyncResult) => service.EndDoSomething(asyncResult); } } public class PortalController : AsyncController { public void NewsAsync(string someInputValue) { AsyncManager.OutstandingOperations.Increment(); var myService = new MyService(); myService.DoSomethingAsync(someInputValue).ContinueWith((task) => { AsyncManager.Parameters["data"] = task.Result; AsyncManager.OutstandingOperations.Decrement(); }, TaskScheduler.FromCurrentSynchronizationContext()); } public ActionResult NewsCompleted(byte[] data) { return View("News", new ViewStringModel { NewsData = data }); } } 

您的两种方法都在进行所谓的同步异步。 那就是以同步方式执行异步方法。 更好的方法是使用TaskCompletionSource构建自己的异步方法来恢复数据。 我没有测试过这个,但你应该可以这样做:

 public Task GetSomeDataAsync() { var tcs = new TaskCompletionSource(); IAsyncResult result = myServiceClient.BeginDoSomething(someInputValue, x => { try { var data = _pdfCreatieService.EndCreateForPreview(result); tcs.SetResult(data); } catch(Exception ex) { tcs.SetException(ex); } }, null); return tcs.Task; } 

然后使用它只需这样做

 GetSomeDataAsync().ContinueWith(t => /**/); 

此代码将立即返回,一旦异步操作完成, ContinueWith部分将执行。