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

时间:2016-06-14 16:52:27

标签: c# windows-services async-await task uwp

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

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

var obj = await callAsync(10);

问题是: 如果此调用时间少于1分40秒(100秒),那么一切正常。但是如果它需要超过1分40秒,那么将发生异常" TaskCanceledException:任务被取消"。

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

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

await Task.delay(200000); 

它运作正常。

此服务是通过VS2015"添加服务参考"添加的。我还附上了#34;到服务器和服务器继续运行并在日志之前和之后在控制台中打印(以确认一切正常)。

我错过了什么?我需要更改哪些配置以及在哪里进行更改,以便任务可以运行超过1分40秒?

CODE:

服务器伪代码示例:

接口文件:

[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有关。

2 个答案:

答案 0 :(得分:2)

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

我在尝试期间发现的是:

  • 如果项目在WinForms中,一切都按预期工作。这意味着,这个100秒的限制是UWP“功能”;
  • 如果将SendTimeout减少到小于100秒,它将使用相应的计时器抛出TimeoutException;
  • 这不是Windows服务所独有的。当与SOAP Webservice实现通信时也会发生这种情况;
  • 这似乎只有在您执行需要与服务引用“外部通信”的任务时才会发生。如果你有一个超过100秒的“内部”任务,它会按预期工作(例如等待Task.delay(250000))。

我是如何解决这个问题的?

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

这是我采取的步骤:

  1. 我更新了现有的服务请求(call(int val))以接受另一个参数Guid,因此我可以确定我想要执行“轮询”的请求;
  2. 我在服务中为InquireAboutCurrentRequest创建了一个额外的请求,该请求也接受了一个GUID参数,并返回了一个int;
  3. 我使用新请求更新了UWP应用程序的服务参考;
  4. 我用try catch调用了“await call(val,guid)”。我之所以这样做是因为90%的这些电话会在不到30秒的时间内恢复*;
  5. 在catch中我添加了一个“If”,用于检查异常是否为CancelTask​​,如果是,我调用“await InquireAboutCurrentRequest(guid)”;
  6. 此方法在Windows服务中不断检查其他操作是否已结束并每隔X秒休眠一次。由于第一次通话的总时间最多只能是2分钟,所以我只需要等待20秒;
  7. 之后我会相应地处理结果,但至少这次我知道我已经“等了2分钟”作出回应。
  8. 还有其他可能的解决方案,例如套接字,我还没试过,可以使用。

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

答案 1 :(得分:0)

我确定不确定 - 但可能更好地使用Task.WhenAll而不是等待?