【问题标题】:Using Task.WhenAny() in a web service在 Web 服务中使用 Task.WhenAny()
【发布时间】:2013-09-04 19:27:00
【问题描述】:

我有一个需要尽快返回数据的网络服务。我有两个可以查询的来源。我想返回使用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;
        }
   }
}

当我在调试模式下执行此操作并运行我的网络服务时,网络服务将按预期返回“2”。但是 3 秒后,当 CallOne() 完成时,我的应用程序因“调用堆栈仅包含外部代码”而崩溃,并出现空引用异常“对象引用未设置为对象的实例”。我的我的堆栈跟踪是:

 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()

【问题讨论】:

  • 修改代码以包含所有文件

标签: c# web-services asynchronous task-parallel-library async-await


【解决方案1】:

首先,您需要在 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;
}

【讨论】:

  • 已接受。添加 ConfigureAwait(false) 就可以了。我猜默认情况下,较慢任务的执行被返回到已经完成的主线程?非常感谢!
  • 它正在返回一个已经完成的请求上下文(不是“主线程”),是的。另外,一定要设置targetFramework
猜你喜欢
  • 2014-08-23
  • 2011-08-27
  • 1970-01-01
  • 2011-01-18
  • 2011-01-12
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多