0

我有一个应在 30 分钟内完成的应用程序。应用程序的组件使用线程池运行。

所以

//queue first all the components
//when the Collect method for each of the components finishes it will set the event
ManualResetEvent serverEvent = new ManualResetEvent(false);
sectionsCompleted.Add(serverEvent);
ThreadPool.QueueUserWorkItem(serverInfo.Collect,"ServerInfo ");

ManualResetEvent cpuEvent= new ManualResetEvent(false);
sectionsCompleted.Add(cpuEvent);
ThreadPool.QueueUserWorkItem(cpuInfo.Collect,"CPUInfo ");

//then wait for all the components to finish
WaitHandle.WaitAll(sectionsCompleted.ToArray());

所以逻辑是调用 ThreadPool 中的所有组件,并使用 ManualResetEvent 类向主线程发出组件已完成的信号。

现在我想使用 ElapsedEvent 处理程序来确保代码在某个时间范围内(比如 30 分钟)优雅地完成。因此,30 分钟后,如果仍有一些线程在运行,我想中止它们。

所以我的问题是会调用ElapsedEventHandler委托吗?还是主线程会等待WaitHandle.WaitAll(sectionsCompleted.ToArray())

有没有其他方法可以实现在一段时间间隔后停止线程池中的所有线程的功能。

4

2 回答 2

1

如果您设置计时器,计时器的事件处理程序,并在上述代码之前(或至少在 之前)启动计时器,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 版本),那么Tasks 将非常适合此操作,因为您可以使用 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());
}
于 2014-05-06T09:26:15.243 回答
1

ElapsedEventHandler 委托被调用了吗?

yes

主线程会等待 WaitHandle.WaitAll(sectionsCompleted.ToArray()) 吗?

yes

但是您需要在线程中向事件处理程序发出信号(如 cpuInfo.Collect),在 .net 4.5 中,您也可以使用 CancellationTokenSource(TimeSpan) 在一段时间后取消线程。

顺便说一句:您应该将 WaitHandle.WaitAll(sectionsCompleted.ToArray()) 放在非 UI 线程中,否则它会阻塞您的 UI。

于 2014-05-06T09:28:47.137 回答