c# - 在指定时间段后停止方法

标签 c# multithreading c#-4.0 active-directory task-parallel-library

我在从 Active Directory 获取的 PC 列表上运行并行操作。我用这个方法来检查电脑状态,比如电脑是否在线,或者特定目录是否存在。但是,由于这些有时很慢的操作的性质,我想包含一个超时,以便我的应用程序可以继续运行。

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);
    var thread = new Thread(() => result = F());
    thread.Start();
    Completed = thread.Join(Timeout);
    if (!Completed) thread.Abort();
    return result;
}

这在大部分情况下都有效,但处理使用量似乎有点飙升,在少数情况下我遇到了内存不足异常。于是,我改用tasks的方式,希望ThreadPool能解决上述问题:

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);

    var timedTask = Task.Factory.StartNew(() => result = F());
    Completed = timedTask.Wait(Timeout);

    return result;
}

但是,我有一种感觉,我只是用挂起等待这些可能很长的任务完成的进程填满了 ThreadPool。由于我将任务的函数作为参数传递,因此我看不到使用取消 token 的方法。但是,我在这些类(class)上的经验非常有限,我可能会遗漏一些高级技术。

下面是我使用上述方法的方式:

bool isReachable; // Check if the directory exists (1 second)
MethodTimeout(() => Directory.Exists(startDirectory), 1000, out isReachable);

请注意,我只是在通过 WMI 调用(也使用 MethodTimeout)确认 PC 在线后才运行上述检查。通过我的早期测试,我知道在此之前检查目录效率低下。

我也愿意用更好的方法取代这种方法。

最佳答案

我可能是坏消息的先兆,但这种情况比大多数人想象的要难处理得多。看来您已经意识到了这一点。使用协作取消机制很好,但您必须能够真正取消耗时的操作才能有效地使用它们。问题是 Directory.Exists 无法取消。

第一个解决方案的问题是您要中止一个线程。这不是一个好主意,因为它会在不可预测的点停止线程。这可能会导致注入(inject)中止时在调用堆栈上执行的任何内容的数据结构损坏。在这种特殊情况下,如果 Thread.Abort 调用实际上挂起,我不会感到惊讶。原因是在非托管代码中执行时,中止通常会延迟。很可能 Directory.Exists 遵循非托管模块。如果是这种情况,则中止无论如何都不会起作用。

第二个解决方案的问题是您将任务孤立。 Directory.Exists 调用仍将在某处的线程池线程上执行。这是因为您实际上并没有取消它。

老实说,我真的不知道该怎么办。缺少可取消的 Directory.Exists 方法是非常有问题的。我的第一个想法是尝试从要测试的目录中写入或读取,作为检查其存在的代理。 FileStream 类确实有可取消的操作。事实上,许多方法甚至接受 CancellationToken 作为参数。或者您可以关闭 FileStream,这也会取消所有挂起的操作。另一种选择可能是使用 Win32 API 函数来执行 IO。然后你可以打电话CancelSynchronousIo如有必要。

我知道这不是一个很好的答案,因为我真正做的只是告诉你什么不能做,但没有提供明确的解决方案。关键是最好的解决方案从可取消的操作开始。不幸的是,一些 BCL 类即使应该提供这些也没有提供。

关于c# - 在指定时间段后停止方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19718485/

相关文章:

c# - 如何在 ASP .NET 的 configureServices 函数中获取连接字符串

c# - C# 和 C++ 中的++i 运算符区别

c# - 非 Win Form C# 应用程序中的同步事件处理程序线程执行

c++ - 将程序 (.exe) 作为线程执行 - Window C++

c# - 用 protobuf 替换 binaryformatter

c# - 在 C# Com 包装器中使用 native dll 并在 silverlight 中使用 dll

c# - 比较 typeof(Type) 和 System.RuntimeType

c# - 如何使用 ProgressBar 更新正确实现 BackgroundWorker?

xml - 如何阅读 Twitter 提要

linq - 具有数据对象的动态LINQ