c# - 在实现时间受限的方法时,我应该中止工作线程还是让其自行运行?

标签 c# multithreading wcf-lob-adapter

我目前正在为现有应用程序编写基于 Web 服务的前端。为此,我使用了 WCF LOB Adapter SDK ,它允许创建自定义 WCF 绑定(bind),将外部数据和操作公开为 Web 服务。

SDK 提供了一些接口(interface)来实现,其中一些方法有时间限制:实现预计在指定的时间跨度内完成其工作或抛出 TimeoutException。 .

调查使我想到了“Implement C# Generic Timeout”这个问题,该问题明智地建议使用工作线程。有了这些知识,我可以写:

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,
    int maxChildNodes, TimeSpan timeout)
{
    Func<MetadataRetrievalNode[]> work = () => {
        // Return computed metadata...
    };

    IAsyncResult result = work.BeginInvoke(null, null);
    if (result.AsyncWaitHandle.WaitOne(timeout)) {
        return work.EndInvoke(result);
    } else {
        throw new TimeoutException();
    }
}

但是,对于超时时如何处理工作线程,目前还没有达成共识。可以像上面的代码一样忘记它,也可以中止它:

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,
    int maxChildNodes, TimeSpan timeout)
{
    Thread workerThread = null;
    Func<MetadataRetrievalNode[]> work = () => {
        workerThread = Thread.CurrentThread;
        // Return computed metadata...
    };

    IAsyncResult result = work.BeginInvoke(null, null);
    if (result.AsyncWaitHandle.WaitOne(timeout)) {
        return work.EndInvoke(result);
    } else {
        workerThread.Abort();
        throw new TimeoutException();
    }
}

现在,中止线程被广泛认为是错误的。它会中断正在进行的工作、泄漏资源、混淆锁定,甚至不能保证线程会真正停止运行。也就是说,HttpResponse.Redirect() 每次调用时都会中止一个线程,IIS 似乎对此非常满意。也许它准备好以某种方式处理它。我的外部应用程序可能不是。

另一方面,如果我让工作线程自行其是,除了资源争用增加(池中可用线程减少)之外,内存不会泄漏,因为 work.EndInvoke() 永远不会被调用?更具体地说,work 返回的 MetadataRetrievalNode[] 数组不会永远保留吗?

这只是两害相权取其轻的问题,还是有办法不中止工作线程并仍然回收 BeginInvoke() 使用的内存?

最佳答案

嗯,先关Thread.Abort几乎没有以前那么糟糕了。 2.0 中对 CLR 进行了多项改进,修复了与中止线程相关的几个主要问题。请注意,它仍然很糟糕,所以避免它是最好的做法。如果您必须求助于中止线程,那么至少您应该考虑从中止起源的地方拆除应用程序域。在大多数情况下,这将具有令人难以置信的侵入性,并且无法解决非托管资源可能出现的损坏问题。

除此之外,在这种情况下中止还会产生其他影响。最重要的是您正试图中止 ThreadPool线。我真的不确定最终结果会是什么,它可能会有所不同,具体取决于所使用的框架版本。

最好的做法是让您的 Func<MetadataRetrievalNode[]>委托(delegate)在安全点轮询一个变量,看它是否应该自行终止执行。

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex, int maxChildNodes, TimeSpan timeout)
{
    bool terminate = false;

    Func<MetadataRetrievalNode[]> work = 
      () => 
      {
        // Do some work.

        Thread.MemoryBarrier(); // Ensure a fresh read of the terminate variable.
        if (terminate) throw new InvalidOperationException();

        // Do some work.

        Thread.MemoryBarrier(); // Ensure a fresh read of the terminate variable.
        if (terminate) throw new InvalidOperationException();

        // Return computed metadata...
      };

    IAsyncResult result = work.BeginInvoke(null, null);
    terminate = !result.AsyncWaitHandle.WaitOne(timeout);
    return work.EndInvoke(result); // This blocks until the delegate completes.
}

棘手的部分是如何处理委托(delegate)内部的阻塞调用。显然,您无法检查 terminate标记代理是否处于阻塞调用的中间。但是,假设阻塞调用是从一种固定的 BCL 等待机制( WaitHandle.WaitOneMonitor.Wait 等)发起的,那么您可以使用 Thread.Interrupt “戳”它,应该会立即解锁它。

关于c# - 在实现时间受限的方法时,我应该中止工作线程还是让其自行运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4503757/

相关文章:

c# - char 和 char 有什么区别?

c - 在 POSIX 中执行线程

java - 如何等待 Android runOnUiThread 完成?

asp.net - 在 IIS 中管理后台线程的最佳实践有哪些?

biztalk - 有没有办法手动删除WCF LOB Adapter SDK

c# - C# 中的字符串清理

c# - 尝试实现自定义格式化程序,但从未调用 ICustomFormatter.Format

c# - 如何将 List<Tuple<string, int>> 转换为 string[]?