C# 在指定的时间段后停止方法

C# 在指定的时间段后停止方法,c#,multithreading,c#-4.0,active-directory,task-parallel-library,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 =

我正在从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;
}
然而,我有一种感觉,我只是用挂起等待这些可能很长的任务完成的进程来填充
线程池。因为我将任务的函数作为参数传递,所以我看不到使用取消令牌的方法。然而,我在这些课程上的经验非常有限,我可能缺少一些优秀的技巧

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

bool isReachable; // Check if the directory exists (1 second)
MethodTimeout(() => Directory.Exists(startDirectory), 1000, out isReachable);
请注意,我只是在通过WMI调用(也使用
MethodTimeout
)确认电脑联机后才运行上述检查。通过我早期的测试,我意识到在此之前检查目录是低效的


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

如果您使用的是.Net 4.5,您可以使用:


像这样的东西行吗?您必须要求消费者向您传递一个接受
CancellationTokenSource
Func
Func
将负责在其代码中的适当位置检查
iscancellationrequest

public static T MethodTimeout<T>(Func<T, CancellationTokenSource> F, 
                                 int Timeout, out bool Completed)
{
    T result = default(T);
    var source = new CancellationTokenSource(Timeout);
    var timedTask = Task.Factory.StartNew(() => result = F(source));
    Completed = timedTask.Wait(Timeout);
    if(!Completed) source.Cancel();
    return result;
}
public static T MethodTimeout(函数F,
int超时,out bool已完成)
{
T结果=默认值(T);
var source=新的CancellationTokenSource(超时);
var timedTask=Task.Factory.StartNew(()=>result=F(source));
已完成=timedTask.Wait(超时);
如果(!已完成)source.Cancel();
返回结果;
}

我对第三方物流的了解微乎其微,如果我的代码不太正确,那么很抱歉。我还没有办法用我的旧版本VS.Corrects/edits来测试它欢迎:)

我可能是这里坏消息的先兆,但这种情况比大多数人想象的更难处理。看来你已经了解了这一点。使用协作取消机制是很好的,但是您必须能够实际取消耗时的操作,才能有效地使用它们。问题是无法取消
目录.Exists

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

第二种解决方案的问题是,您将使任务处于孤立状态。该
目录.Exists
调用仍将在某个线程池线程上执行。这是因为你实际上并没有取消它

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


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

顺便说一句,.Net命名惯例规定,方法的参数应该以
小写字母命名。此外,如果您不了解问题所在,盲目改变调用代码的方式很可能不会有任何帮助。你需要找出这些问题的原因。有帮助吗?它采用将任务包装到另一个任务中的方法。你能强迫用户给你一个
Func
,它接受一个
CancellationTokenSource
?我不确定我是否同意“盲目”的说法,但我同意隧道视觉。我知道,在使用Thread.Start时,我会尝试管理两个异常。通过实现threadpool,可以缓解这两个异常。我可能需要缩小并着眼于更大的前景,我最终会这样做。不幸的是,我仅限于.NET4.0。仍然需要知道,将这一个记录下来。您能告诉我,如果我有方法DoWork(),我如何在3秒钟后停止它吗?我不能只写在里面,而我实际上认为这是一个很好的答案,谢谢你解释目录类。今天我将测试这个方向,看看是否可以利用一个可以取消的方法。我可以将它用于我可以直接控制的方法,但无法用于类似Directory.Exists的方法。听起来无论如何我都需要放弃这条路线,所以这可能会很有用。谢谢。Wow不知道
目录。存在
可能需要足够长的时间,您可能想取消它。我不确定你所说的“我的忠诚在于每秒的操作数”是什么意思,但如果你想找到一种方法来限制运行的并发数,请看一看(非BCL,但只是一个小班)。我昨天在大约5500台计算机上运行了这一功能,并朝着
var cts = new CancellationTokenSource(3000); // Set timeout

Task.Run(() =>
{
    while (!cts.Token.IsCancellationRequested)
    {
        // Doing Work...
    }

}, cts.Token);
public static T MethodTimeout<T>(Func<T, CancellationTokenSource> F, 
                                 int Timeout, out bool Completed)
{
    T result = default(T);
    var source = new CancellationTokenSource(Timeout);
    var timedTask = Task.Factory.StartNew(() => result = F(source));
    Completed = timedTask.Wait(Timeout);
    if(!Completed) source.Cancel();
    return result;
}