C#ThreadPool和ElapsedEventHandler

时间:2014-05-06 08:58:46

标签: c# multithreading threadpool eventhandler

我的应用程序应在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 Handler确保代码在某个时间范围内(例如30分钟)正常完成。所以30分钟后如果还有一些线程在运行,我想中止它们。

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

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

2 个答案:

答案 0 :(得分: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版本)那么Task将非常适合这个,因为你可以使用CancellationToken允许你在没有完成的情况下优雅地杀死每个任务。有关如下内容的信息,请参阅MSDN: Task Cancellation。如果您不能使用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());
}

答案 1 :(得分:1)

根本调用ElapsedEventHandler委托?

yes

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

yes

但是你需要在线程中发出eventhandler信号(比如cpuInfo.Collect), 在.net 4.5中,你也可以使用CancellationTokenSource(TimeSpan)在一段时间后取消线程。

btw:你应该把WaitHandle.WaitAll(sectionsCompleted.ToArray())放在非ui线程中,否则会阻塞你的UI。