WCF REST Not processed asynchronously

We are currently implementing the new WCF REST service in IIS for our site, and on several pages we can make multiple AJAX calls using jQuery asynchronously. The problem is that it looks like WCF (server side) is running synchronously.

When loading the page, we make 3 separate calls to 3 different methods. Using logging, I see that they all fall into the global.asax file within about 5 ms of each other. From there, logging shows that everything runs in the order in which they exit global.asax (not necessarily in the order in which we made calls from the page using javascript). I expected each call to receive its own flow and return individually. Even when you attach to the debugger, I see that it will not execute the next method until I move on to the current method.

Below are the operating contracts for the three methods that I thought: "I used to use the asynchronous model.

[OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListUserPreferences" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListUserPreferences(AsyncCallback callback, object state); Result<List<Data.EnumerationItem<UserPreferenceType>>> EndListUserPreferences(IAsyncResult asyncResult); [OperationContract(Name = "GetUserSecure", AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/GetUser" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginGetUser(AsyncCallback callback, object state); Result<Data.User> EndGetUser(IAsyncResult asyncResult); [OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListWithAttributes" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListWithAttributes(int index, int pageSize, AsyncCallback callback, object state); Result<PagedCollection<Data.Attribute>> EndListWithAttributes(IAsyncResult asyncResult); 

Here is an example of one of the implementations in the service.

  public IAsyncResult BeginGetUser(AsyncCallback callback, object state) { var asyncResult = new CompletedAsyncResult<Result<Data.User>>(state); asyncResult.Result = new Result<Data.User>(); asyncResult.Result.Value.UserId = Guid.Empty; asyncResult.Result.Value.DisplayName = "asdfasd"; asyncResult.IsCompleted = true; callback(asyncResult); return asyncResult; } public Result<Data.User> EndGetUser(IAsyncResult asyncResult) { return ((CompletedAsyncResult<Result<Data.User>>)asyncResult).Result; } 

Here are the attributes that we have in the service implementation class.

 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)] 

Can someone explain why they execute synchronously and what I need to do, or at least point me in the direction of what I need to do to get them to execute asynchronously?

UPDATE

I took part of Matt's answer and shifted my logic to calls to the End function and watched this blog post about how he made it a little closer Downloading large files Self-hosted WCF Recreation Service . However, I could not get End methods to use its technique. I'm starting to think that I'm wrong too. Since I look at the logs, my user authentication service is executed before each service call, which happens before any operations with the asynchronous method even work. After further study, I looked at the ThreadId of each request included in IIS, and then performed the operations. It seems that WCF uses only one worker thread ... period. It doesn't matter how many requests I send to IIS. For example, if I send 3 requests, I see that they all come in at different times (in milliseconds of each other), and everyone gets their own stream. But then it seems that WCF just queues them and runs them in that order, because they all run on the same thread, including authentication service calls.

+7
source share
2 answers

It seems to me, from your example, how you do all the work before the "Begin" call ends; this is a common error that I discovered while studying an asynchronous pattern.

Although I am not familiar with its application in WCF, the Async model is more typically a Begin method that starts a new thread with an IAsyncResult object, which should be updated with this new thread. To โ€œcompleteโ€ an action when IsCompleted set to true , it is expected that the original caller will pass the original IAsyncResult back to the appropriate End method, which returns the result. The trivial implementation is as follows:

  static Func<string> getUser; public static IAsyncResult BeginGetUser(AsyncCallback callback, object state) { getUser = () => { Thread.Sleep(2000); return "finished"; }; return getUser.BeginInvoke(callback, state); } public static string EndGetUser(IAsyncResult asyncResult) { return getUser.EndInvoke(asyncResult); } 

Calls to him may look like this:

 var result = BeginGetUser(null, null); string value = EndGetUser(result); 

Of course, this is a trivial case: to quote http://kennyw.com/work/indigo/258 : "If you are not doing something that is" initially asynchronous ", then you should not use AsyncPattern = true."

Fortunately, with C # 5.0 or Async CTP, released by Microsoft, the asynchronous .Net template can be a thing of the past.

+2
source

To make things asynchronous, you usually compose them with other asynchronous things. If you perform synchronous actions only with your asynchronous methods, it makes no sense to use the async template. For example, if all your code is placed in a thread pool, you did nothing, because you returned the thread to the thread pool, moving your code asynchronously, but stole it directly from ASP.NET, starting your work there.

If you make a database call to get the user, and your database supports asynchronous operations, you can create something like this:

 public IAsyncResult BeginGetUser(AsyncCallback callback, object state) { var taskFunc = Task<DbReader>.Factory.FromAsync(db.BeginGetUser, db.EndGetUser); return taskFunc.ContinueWith(task => { var reader = task.Result; reader.Read(); return new Data.User { DisplayName = reader["displayName"] as string, UserId = Guid.Parse(reader["userId"] as string), } } ); } public Result<Data.User> EndGetUser(IAsyncResult asyncResult) { return (Task<User>)(asyncResult).Result; } 

Let me emphasize something: asynchronous programming is complicated. With Task, this gets a little easier, but you still have to wrap your head around sequels. Debugging is a difficult task and one wrong move, and your code just disappears and you don't know why. If you miss an exception, it will appear in the finalizer thread, and you wonโ€™t know why. In addition, in order to perform asynchronous programming, you must put pressure on multi-threaded programming, full of dangers.

Make sure that you really understand what is happening before trying to switch to asynchronous. If your code is synchronous in nature, then its asynchrony does not buy you very much, if it is not a lengthy process, and you are fine, deploy another thread to process it. People use asynchronous handlers so that IIS (or any other web server) can return its stream to serve other requests; as I mentioned earlier, if you then just go in a thread from a thread pool, you steal that thread from IIS and you won't see any scalability gains. However, if you simply create new threads, you open yourself up to denial of service attacks.

0
source

All Articles