Using Task.WhenAny () in a web service

I have a web service that should return data as quickly as possible. I have two sources that I can request. I want to return using Task.WhenAny() to request these sources and return the element that is returned first. When I run my code in a local local environment, the code will successfully return data from the fastest task. BUT, when a slower task returns, it crashes my IIS Express workflow with the exception of a null reference.

MyService.asmx

 <%@ WebService Language="C#" CodeBehind="MyService.asmx.cs" Class="MyWebService.MyService" %> 

MyService.asmx.cs

 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; namespace MyWebService { /// <summary> /// Summary description for Service1 /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. // [System.Web.Script.Services.ScriptService] public class MyService : System.Web.Services.WebService { private WebCore _webService; public MyService() { _webService = new WebCore(); } [WebMethod] public int TestAsync() { return _webService.TestInt(); } } } 

WebCore.cs

 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Threading; using System.Threading.Tasks; namespace MyWebService { public class WebCore { public WebCore() {} public int TestInt() { return AsyncCaller().Result; } private CancellationTokenSource cts; async Task<int> CallOne(CancellationToken ct) { await Task.Delay(4000); return 1; } async Task<int> CallTwo(CancellationToken ct) { await Task.Delay(1000); return 2; } private async Task<int> AsyncCaller() { cts = new CancellationTokenSource(); var tasks = new List<Task<int>>() { CallOne(cts.Token), CallTwo(cts.Token) }; var completedtasks = await Task.WhenAny(tasks); var res = await completedtasks; cts.Cancel(); return res; } } } 

When I do this in debug mode and start my web service, the web service will return “2” as expected. However, after 3 seconds, when CallOne () exits, my application crashes with "The call stack contains only external code," with the exception of the link "bject reference not installed on the object instance." My trace:

  at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext) at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext) at System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state) at System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state) at System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state) at System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state) at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask) --- End of stack trace from previous location where exception was thrown --- at System.Threading.Tasks.AwaitTaskContinuation.<ThrowAsyncIfNecessary>b__1(Object s) at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 
+4
source share
1 answer

First you need to enable async / await in ASP.NET correctly. To do this, make sure you target .NET 4.5, and then either set httpRuntime.targetFramework to 4.5 , or add appSetting of aspnet:UseTaskFriendlySynchronizationContext set to true .

Then you want to avoid re-entering the request context when populating the async methods:

 async Task<int> CallOne(CancellationToken ct) { await Task.Delay(4000).ConfigureAwait(false); return 1; } async Task<int> CallTwo(CancellationToken ct) { await Task.Delay(1000).ConfigureAwait(false); return 2; } 
+5
source

Source: https://habr.com/ru/post/1500604/


All Articles