如何取消一个挂起的任务?

4
我们有一个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;
}

第三方方法会无限期地阻塞,等待来自不能响应的资源的输入,它以任何方式支持取消,也没有内置超时。我们担心随着服务的运行,这些任务将继续阻塞并缓慢积累,最终消耗大量资源。

这是一个有效的担忧吗?我们需要以某种方式中止/处理这些任务吗?如果是这样,正确的做法是什么呢?

补充说明:所涉及的第三方是Crystal Reports。当要求其打印到需要用户输入的打印机(例如,如果您将其打印到Microsoft XPS文档编写器,则会提示您保存文件的位置)时,它会挂起。所谓“挂起”,是指它尝试显示一个用户提示以获取额外的输入,但由于它在Windows服务中,因此没有人可以看到用户提示,并且它会永远等待一个人告诉它如何打印。我们允许最终用户配置服务尝试打印到哪个打印机,并且没有真正的方法可以短暂地判断给定的打印机是否需要额外的输入,除了尝试将其打印到该打印机。


Foo是什么?它是一个阻塞线程的普通方法吗?如果您需要中止任务,这可能会有所帮助:https://dev59.com/rm445IYBdhLWcg3wmLZd#19311606 - Meirion Hughes
3
你的问题基本上是“我已经依赖于实现不佳、随机挂起的第三方软件,我该怎么办?” 在我看来,问题应该通过“一开始就不要这么做”来解决。如果你的第三方软件在解决问题方面表现不佳,那么让他们修复糟糕的设计,或者找到替代品。像杀死线程这样的危险变通方法似乎是在处理过程中太晚才解决问题。 - Eric Lippert
哦,太常见了!我已经做了很多显示一些UI提示的dll。当然最终需要修复它,但我并不总是有时间去做正确的解决方案。然后就会出现各种问题——注册表修改、临时文件、批处理等等。这不是“糟糕的设计”,只是需要修复的错误。当你的应用程序很复杂时,很难找到所有UI提示的出现位置。 - TarmoPikaro
2个回答

3
我们的担忧是,随着服务运行,这些任务将继续阻塞并逐渐积累。
这是一个合理的担忧。您可以通过在使用小堆栈的特殊线程池上启动这些任务来减少其后果。这样会减少内存使用量。但这不是一个完整的解决方案。如果您的应用程序必须长时间运行(不是为了几个小时的使用而设计的GUI应用程序),那么这种解决方案将是不可接受的,因为最终应用程序将遭受资源耗尽的问题。
.NET没有终止不合作线程的方法。您需要在自己的进程中运行这些操作。然后您可以安全地终止这些进程。
使用AppDomains可能也是安全的,但这不是确定的。当中止AppDomain和其中的线程时,可能会破坏每个进程状态。此外,并非所有线程都可以中止,特别是IO操作。
使用单独的进程也不能保证终止不会导致状态损坏。但在实践中,大多数易损坏的状态都存在于内存中。进程终止很可能会清除所有不一致的状态。

有一个名为 thread.abort 的函数,它也被 IIS 使用。而且 IIS 可以运行数周甚至数月。 - Riki
1
IIS仅在明确定义的点上使用Thread.Abort来终止当前线程。已知Thread.Abort是“邪恶”的,不能使用。 - usr

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接