我有一个需要尽快返回数据的Web服务。我有两个可以查询的来源。我想返回使用Task.WhenAny()
来查询这些源并返回首先返回的项。当我在本地开发环境中运行我的代码时,代码将成功从最快的任务返回数据但是当较慢的任务返回时,它会使我的IIS Express工作进程崩溃并引用空引用异常。
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;
}
}
}
当我在调试模式下执行此操作并运行我的Web服务时,Web服务将按预期返回“2”。但是3秒后,当CallOne()完成时,我的应用程序崩溃,“调用堆栈只包含外部代码”,空引用异常“bject reference未设置为对象的实例”。我的堆栈跟踪是:
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()
答案 0 :(得分:5)
首先,您需要在ASP.NET中正确启用async
/ await
。为此,请确保您的目标是.NET 4.5,然后是either set httpRuntime.targetFramework
to 4.5
or add an appSetting
of aspnet:UseTaskFriendlySynchronizationContext
set to true
。
接下来,您希望避免在完成async
方法时重新输入请求上下文:
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;
}