等待服务任务获取 TaskCanceledException:任务已取消

Awaiting a service task gets TaskCanceledException: A task was canceled

我有一个应用程序 (UWP - Win10) 和一个 Windows 服务。

服务在后台 运行ning,它们都是用 C# 开发的。 "callAsync" 是服务上的方法。我正在使用 await 在客户端调用它。

var obj = await callAsync(10);

问题是: 如果此调用花费的时间少于 1 分钟 40 秒(100 秒),则一切正常。但是如果超过1min40s,那么就会出现异常"TaskCanceledException: A task was canceled".

我搜索了 SO 和网络,但仍然找不到任何关于如何解决此 "timeout" 问题的指示。我已经在应用程序和服务 app.config 上添加了所有 "open/close/receive/send" 超时标志,尽管在这种情况下抛出的异常是不同的。

如果我在客户端尝试简单延迟:

await Task.delay(200000); 

它工作正常。

此服务是通过 VS2015 添加的 "Add Service Reference"。我还 "attached" 到服务器,服务器保持 运行ning 并在日志之前和之后在控制台中打印(以确认一切正常)。

我错过了什么?我需要更改什么配置和在哪里才能让任务 运行 超过 1 分 40 秒?

代码:

服务器伪代码示例:

接口文件:

[ServiceContract(Namespace="http://.....")]
interface ICom {

   [OperationContract]
   int call(int val);

}

Service.cs

    public ServiceHost serviceHost = null;
    public BlaBlaWindowsService()
    {
        ServiceName = "BlaBlaWindowsService";
    }

    public static void Main()
    {
        ServiceBase.Run(new BlaBlaWindowsService());
    }


    protected override void OnStart(string[] args)
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
        }

        serviceHost = new ServiceHost(typeof(BlaBlaService));

        serviceHost.Open();
    }

    protected override void OnStop()
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
            serviceHost = null;
        }
    }
}

[RunInstaller(true)]
public class ProjectInstaller : Installer
{
    private ServiceProcessInstaller process;
    private ServiceInstaller service;

    public ProjectInstaller()
    {
        process = new ServiceProcessInstaller();
        process.Account = ServiceAccount.LocalSystem;
        service = new ServiceInstaller();
        service.ServiceName = "BlaBlaWindowsService";
        Installers.Add(process);
        Installers.Add(service);
    }
}

BlaBlaService.cs

class TPAService : ITPAComunication {

   public int call(int val) {

      System.Threading.Thread.Sleep(200000)
      return 0;
   }

}

App.config 文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<binding name="ServiceTimeout" closeTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00"/>
</bindings>
        <services>
          <service name="BlaBla.Service.Service"
                   behaviorConfiguration="ServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8000/BlaBla/service"/>
              </baseAddresses>
            </host>
            <endpoint address=""
                      binding="basicHttpBinding"
                      bindingConfiguration="ServiceTimeout"
                      contract="BlaBla.Service.ICom" />
            <endpoint address="mex"
                      binding="mexHttpBinding"
                      contract="IMetadataExchange" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="ServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>
              <serviceDebug includeExceptionDetailInFaults="False"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>

App伪代码示例:

System.ServiceModel.EndpointAddress epa = new System.ServiceModel.EndpointAddress("http://localhost:8000/blabla/service");

System.ServiceModel.BasicHttpBinding bhb = new System.ServiceModel.BasicHttpBinding();

Timespan t = new TimeSpan(0, 10, 0);

bhb.SendTimeout = t; bhb.ReceiveTimeout =t; bhb.OpenTimeout = t; bhb.CloseTimeout = t;

Blabla.ComunicationClient com = new Blabla.ComunicationClient(bhb, epa);

var obj = await com.callAsync(int val);

return obj;

更新 #1

这种情况只发生在UWP中。我创建了一个类似的 WinForms 项目,一切都按预期进行。这意味着它可能与UWP有关。

经过多次尝试,操作不同的配置文件,我还没有找到关于如何删除 100 秒超时限制的解决方案。为了解决这个具体问题,我实施了一个对策。

我在尝试过程中发现的是:

  • 如果项目在 WinForms 中,一切都按预期进行。这意味着,这个 100 秒限制是一个 UWP "Feature";
  • 如果将 SendTimeout 减少到小于 100 秒,它将抛出一个带有相应计时器的 TimeoutException;
  • 这不是 Windows 服务独有的。与 SOAP Web 服务实现通信时也会发生这种情况;
  • 这似乎只有在您执行需要 "external communication" 并提供服务参考的任务时才会发生。如果您有一个需要超过 100 秒的 "internal" 任务,它会按预期工作(例如等待 Task.delay(250000))。

我是怎么解决的?

在 C# SO 频道聊天后,@Squiggle 提出了一种轮询方法,这就是我成功实施和测试的方法。

这些是我采取的步骤:

  1. 我更新了现有的服务请求 (call(int val)) 以接受另一个参数,一个 Guid,这样我就可以确定我想要执行哪个请求 "polling";
  2. 我在服务中为 InquireAboutCurrentRequest 创建了一个额外的请求,它接受了一个 GUID 参数,并且 returned 了一个 int;
  3. 我用新请求更新了 UWP 应用中的服务参考;
  4. 我用 try catch 调用了 "await call(val,guid)"。我这样做是因为 90% 的调用 return 不到 30 秒*;
  5. 在 catch 中我添加了一个 "If" 检查异常是否是 CancelTask​​,如果我调用 "await InquireAboutCurrentRequest(guid)";
  6. 此方法在 windows 服务中持续检查其他操作是否已结束并每隔 X 秒休眠一次。由于第一次调用的总时间最多只能是2分钟,所以我只需要等待20秒;
  7. 之后我会相应地处理结果,但至少这次我知道我有 "waited 2 minutes" 的回应。

还有其他可能的解决方案,例如套接字,我没有尝试过,但可以。

* 如果所有请求都超过100秒,我建议从请求开始就使用轮询的方式,而不是等待try/catch。

我不确定 - 但使用 Task.WhenAll 代替 await 可能更好?