如何取消挂起的任务?
How can I cancel a hung Task?
我们有一个 Windows 调用第三方方法的服务,如果最终用户配置不当,该方法可能会挂起,这很难事先测试。我们通过超时调用任务中的方法来处理此风险:
private int? FooWithTimeout()
{
var timeout = TimeSpan.FromMinutes(1);
var task = Task.Run(Foo);
if (!task.Wait(timeout))
{
Log("Foo timed out...");
return null;
}
if (task.IsFaulted)
{
Log("Foo threw an exception...");
return null;
}
return task.Result;
}
第 3 方方法永远阻塞等待来自无法响应的资源的输入。它不 支持以任何方式、形状或形式取消,也没有内置超时。我们担心的是,随着服务的运行,这些任务会不断阻塞并慢慢积累,最终消耗大量资源。
这是一个合理的担忧吗?我们需要以某种方式 abort/dispose 任务吗?如果可以,正确的做法是什么?
附录: 有问题的第 3 方是 Crystal 报告。当要求打印到需要用户进行某种额外输入的打印机时,它会挂起(例如,如果打印到文件,Microsoft XPS Document Writer 将提示您保存文件的位置)。挂起,我的意思是它会尝试显示用户提示以获取额外的输入,但它在 Windows 服务中,因此没有人会看到用户提示,它会永远等待人类告诉它如何打印.我们允许最终用户配置服务尝试打印到哪台打印机,实际上没有任何方法可以判断给定打印机是否需要额外的输入而不是尝试打印到它。
Our concern is that as the service runs these tasks will continue blocking and slowly accumulate
这是一个合理的担忧。您可以通过在使用小堆栈的特殊线程池上启动这些任务来减少这种情况的后果。这样内存使用量就会减少。但这不是一个完整的修复。如果您的应用程序必须长时间运行(而不是打算使用几个小时的 GUI 应用程序),那么这个解决方案将被证明是不可接受的,因为最终应用程序将遭受资源耗尽。
.NET 无法终止不合作的线程。您需要 运行 在他们自己的流程中执行这些操作。然后您可以安全地终止这些进程。
使用 AppDomains 也可能是安全的,但不太确定。当 AppDomain 和其中的线程被中止时,每个进程的状态可能会被破坏。此外,并非所有线程都可以中止。特别是 IO 操作。
使用单独的进程也不能保证终止不会导致状态损坏。但实际上,大多数易损坏的状态都存在于内存中。进程终止可能会清除所有不一致的状态。
一种方法是创建单独的线程来监视任何新创建的 windows - 如果创建任何新的 window - 线程将尝试强制关闭它们。
枚举windows:
How to enumerate all windows belonging to a particular process using .NET?
关闭不需要的 windows:
How to use WM_Close in C#?
可能行不通,只是一个建议...:-)
我们有一个 Windows 调用第三方方法的服务,如果最终用户配置不当,该方法可能会挂起,这很难事先测试。我们通过超时调用任务中的方法来处理此风险:
private int? FooWithTimeout()
{
var timeout = TimeSpan.FromMinutes(1);
var task = Task.Run(Foo);
if (!task.Wait(timeout))
{
Log("Foo timed out...");
return null;
}
if (task.IsFaulted)
{
Log("Foo threw an exception...");
return null;
}
return task.Result;
}
第 3 方方法永远阻塞等待来自无法响应的资源的输入。它不 支持以任何方式、形状或形式取消,也没有内置超时。我们担心的是,随着服务的运行,这些任务会不断阻塞并慢慢积累,最终消耗大量资源。
这是一个合理的担忧吗?我们需要以某种方式 abort/dispose 任务吗?如果可以,正确的做法是什么?
附录: 有问题的第 3 方是 Crystal 报告。当要求打印到需要用户进行某种额外输入的打印机时,它会挂起(例如,如果打印到文件,Microsoft XPS Document Writer 将提示您保存文件的位置)。挂起,我的意思是它会尝试显示用户提示以获取额外的输入,但它在 Windows 服务中,因此没有人会看到用户提示,它会永远等待人类告诉它如何打印.我们允许最终用户配置服务尝试打印到哪台打印机,实际上没有任何方法可以判断给定打印机是否需要额外的输入而不是尝试打印到它。
Our concern is that as the service runs these tasks will continue blocking and slowly accumulate
这是一个合理的担忧。您可以通过在使用小堆栈的特殊线程池上启动这些任务来减少这种情况的后果。这样内存使用量就会减少。但这不是一个完整的修复。如果您的应用程序必须长时间运行(而不是打算使用几个小时的 GUI 应用程序),那么这个解决方案将被证明是不可接受的,因为最终应用程序将遭受资源耗尽。
.NET 无法终止不合作的线程。您需要 运行 在他们自己的流程中执行这些操作。然后您可以安全地终止这些进程。
使用 AppDomains 也可能是安全的,但不太确定。当 AppDomain 和其中的线程被中止时,每个进程的状态可能会被破坏。此外,并非所有线程都可以中止。特别是 IO 操作。
使用单独的进程也不能保证终止不会导致状态损坏。但实际上,大多数易损坏的状态都存在于内存中。进程终止可能会清除所有不一致的状态。
一种方法是创建单独的线程来监视任何新创建的 windows - 如果创建任何新的 window - 线程将尝试强制关闭它们。
枚举windows:
How to enumerate all windows belonging to a particular process using .NET?
关闭不需要的 windows:
How to use WM_Close in C#?
可能行不通,只是一个建议...:-)