等待服务任务获取 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 提出了一种轮询方法,这就是我成功实施和测试的方法。
这些是我采取的步骤:
- 我更新了现有的服务请求 (call(int val)) 以接受另一个参数,一个 Guid,这样我就可以确定我想要执行哪个请求 "polling";
- 我在服务中为 InquireAboutCurrentRequest 创建了一个额外的请求,它接受了一个 GUID 参数,并且 returned 了一个 int;
- 我用新请求更新了 UWP 应用中的服务参考;
- 我用 try catch 调用了 "await call(val,guid)"。我这样做是因为 90% 的调用 return 不到 30 秒*;
- 在 catch 中我添加了一个 "If" 检查异常是否是 CancelTask,如果我调用 "await InquireAboutCurrentRequest(guid)";
- 此方法在 windows 服务中持续检查其他操作是否已结束并每隔 X 秒休眠一次。由于第一次调用的总时间最多只能是2分钟,所以我只需要等待20秒;
- 之后我会相应地处理结果,但至少这次我知道我有 "waited 2 minutes" 的回应。
还有其他可能的解决方案,例如套接字,我没有尝试过,但可以。
* 如果所有请求都超过100秒,我建议从请求开始就使用轮询的方式,而不是等待try/catch。
我不确定 - 但使用 Task.WhenAll 代替 await 可能更好?
我有一个应用程序 (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 提出了一种轮询方法,这就是我成功实施和测试的方法。
这些是我采取的步骤:
- 我更新了现有的服务请求 (call(int val)) 以接受另一个参数,一个 Guid,这样我就可以确定我想要执行哪个请求 "polling";
- 我在服务中为 InquireAboutCurrentRequest 创建了一个额外的请求,它接受了一个 GUID 参数,并且 returned 了一个 int;
- 我用新请求更新了 UWP 应用中的服务参考;
- 我用 try catch 调用了 "await call(val,guid)"。我这样做是因为 90% 的调用 return 不到 30 秒*;
- 在 catch 中我添加了一个 "If" 检查异常是否是 CancelTask,如果我调用 "await InquireAboutCurrentRequest(guid)";
- 此方法在 windows 服务中持续检查其他操作是否已结束并每隔 X 秒休眠一次。由于第一次调用的总时间最多只能是2分钟,所以我只需要等待20秒;
- 之后我会相应地处理结果,但至少这次我知道我有 "waited 2 minutes" 的回应。
还有其他可能的解决方案,例如套接字,我没有尝试过,但可以。
* 如果所有请求都超过100秒,我建议从请求开始就使用轮询的方式,而不是等待try/catch。
我不确定 - 但使用 Task.WhenAll 代替 await 可能更好?