如果您设置计时器,计时器的事件处理程序,并在上述代码之前(或至少在 之前)启动计时器,WaitAll
那么
- 您的计时器
Elapsed
事件将触发,
- 您的主线程将在
WaitAll
但您可以轻松地执行以下操作:
if (!WaitHandle.WaitAll(sectionsCompleted.ToArray(), TimeSpan.FromMinutes(30)))
{
// did not finish in the 30 minute timespan, so kill the threads
}
如果您执行上述操作,您将不必担心同步计时器的事件处理程序(它可能会在完成时尝试杀死线程)和等待 WaitHandles 的 Main 方法(因此可能会在事件处理程序认为线程正在被杀死)。
如果您能够(取决于.NET 版本),那么Task
s 将非常适合此操作,因为您可以使用 aCancellationToken
让您在每个任务尚未完成时优雅地终止它。有关以下内容,请参阅MSDN:任务取消。如果您不能使用Task
,您可以自己连接相同的解决方案。一种可能的技术是使用更多的 WaitHandles(另见下文)。
这种方法还可以让您将 Wait+Cancel 代码移动到单独的线程中。因此,您可以在创建工作线程时立即释放您的 UI 或主代码线程。这还有一个额外的好处,即您还可以从控制线程向 Wait+Cancel 代码的单个实例发送信号以触发过早取消。
// use the same CancellationTokenSource to create all tasks
var tokenSource2 = new CancellationTokenSource();
// for each task, use the following structure
CancellationToken ct = tokenSource2.Token;
var task = Task.Factory.StartNew(() =>
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
bool moreToDo = true;
// make sure any loops and other methods check the ct.IsCancellationRequested regularly
while (moreToDo)
{
if (ct.IsCancellationRequested)
{
// Clean up any resources, transactions etc. here, then...
ct.ThrowIfCancellationRequested();
}
}
}, tokenSource2.Token); // Pass same token to StartNew.
// add each task to the tasks list
tasks.Add(task);
// once all tasks created, wait on them and cancel if they overrun
// by passing the token, another thread could even cancel the whole operation ahead of time
if (!Task.WaitAll(tasks.ToArray(), (int)TimeSpan.FromMinutes(30).TotalMilliseconds,
tokenSource2.Token))
{
// did not finish in the 30 minute timespan, so kill the threads
tokenSource2.Cancel();
try
{
// Now wait for the tasks to cancel
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException ae)
{
// handle any unexpected task exceptions here
}
}
或者在没有任务的 .NET 2.0 中:
// in Main thread ...
ManualResetEvent serverEventCancelled = new ManualResetEvent(false);
cancellationMres.Add(serverEventCancelled);
// Inside the thread, do this regularly - zero timeout returns instantly ...
if (serverEventCancelled.WaitOne(0))
{
// do cancellation and ...
// now set the "completed" waithandle (or do something similar to let Main know we are done)
serverEvent.Set();
return;
}
// In Main thread ...
if (!WaitHandle.WaitAll(sectionsCompleted.ToArray(), TimeSpan.FromMinutes(30)))
{
foreach (var cancellationMre in cancellationMres)
{
cancellationMre.Set();
}
WaitHandle.WaitAll(sectionsCompleted.ToArray());
}