我正在从Active Directory中获取的PC列表上运行并行操作。我正在使用此方法来检查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;
}
这在很大程度上起作用,但处理用量似乎有点飙升,在少数情况下我遇到了Out of Memory异常。所以,我改变了使用任务的方法,希望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
的进程,这些进程正在等待这些可能很长的任务完成。由于我将任务的函数作为参数传递,因此我没有看到使用取消令牌的方法。但是,我对这些课程的经验非常有限,我可能会遗漏一些优秀的技巧。
以下是我一直使用上述方法的方法:
bool isReachable; // Check if the directory exists (1 second)
MethodTimeout(() => Directory.Exists(startDirectory), 1000, out isReachable);
快速说明一下,在我通过WMI呼叫(也使用MethodTimeout
)确认PC在线后,我才进行上述检查。通过我的早期测试,我知道在此之前检查目录会导致效率低下。
我也愿意用更好的方法取代这种方法。
答案 0 :(得分:6)
我可能是这里坏消息的先兆,但这种情况比大多数人想象的要难得多。看来你已经开始接受这个了。使用协作取消机制都很好,但您必须能够实际取消耗时的操作才能有效地使用它们。问题是Directory.Exists
无法取消。
您的第一个解决方案的问题是您正在中止线程。这不是一个好主意,因为它会在不可预测的点停止线程。这可能导致在中止注入时在调用堆栈上执行的任何事情的数据结构损坏。在这种特殊情况下,如果Thread.Abort
电话实际挂起,我不会感到惊讶。原因是当执行处于非托管代码时,中止通常会延迟。很可能Directory.Exists
遵循非托管模块。如果是这种情况,那么中止无论如何都不会起作用。
第二种解决方案的问题在于您将任务孤立。那个Directory.Exists
调用仍然会在某个线程池线程上执行。这是因为你还没有真正取消它。
说实话,我不知道该怎么办。缺少可取消的Directory.Exists
方法是非常有问题的。我的第一个想法是尝试从您想要测试的目录中写入或读取作为检查其存在的代理类型。 FileStream
类确实具有可取消的操作。事实上,许多方法甚至接受CancellationToken
作为参数。或者您可以关闭FileStream
,这也将取消任何待处理的操作。另一种选择可能是使用Win32 API函数来执行IO。然后,如有必要,您可以致电CancelSynchronousIo。
我知道这不是一个很好的答案,因为我所做的就是告诉你你不能做什么,但没有提供明确的解决方案。关键是最佳解决方案以可取消操作开始。遗憾的是,有些BCL课程即使在他们应该的时候也不提供这些课程。
答案 1 :(得分:4)
如果您使用的是.Net 4.5,则可以使用CancelAfter
:
var cts = new CancellationTokenSource(3000); // Set timeout
Task.Run(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// Doing Work...
}
}, cts.Token);
答案 2 :(得分:0)
这样的事可以吗?您必须要求消费者通过Func
CancellationTokenSource
。Func
。 IsCancellationRequested
负责在其代码中的适当位置检查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;
}
...
{{1}}
我对TPL的了解很少,如果我的代码不是很正确,那就很抱歉。 Haven没有办法用我的旧版VS测试它。更正/编辑欢迎:)