2010-02-10 30 views
1

我总的线程n00b,我想找出一种方法来先关闭线程,然后通过强制请求。如何阻止线程在代码的特定部分内终止?

我有一个ProcessManager类启动了一堆Process类主题:

public class ProcessManager 
{ 
    private IProcessRepository _processRepository; 
    private List<Thread> _threads = new List<Thread>(); 

    public ProcessManager(IProcessRepository processRepository) 
    { 
     _processRepository = processRepository; 
    } 

    public void StartAllProcesses() 
    { 
     foreach (var process in _processRepository.GetActiveProcesses()) 
     { 
      var thread = new Thread(process.ProcessUntilStopped); 
      _threads.Add(thread); 
      thread.Start(); 
     } 
    } 

    public void StopAllProcesses() 
    { 
     // What to do here? 
    } 
} 

public class Process 
{ 
    private bool _stopFlag = false; 

    public void ProcessUntilStoppped() 
    { 
     while (true) 
     { 
      if (_stopFlag) return; 

      // I don't want this call interrupted by Thread.Join(), 
      // but it's important for multiple Processes to be able to DoWork() 
      // simultaneously. 
      DoWork(); 
     } 
    } 

    public void Stop() 
    { 
     _stopFlag = true; 
    } 
} 

如何让我的线程不被打断同时呼吁DoWork(),而是等到下一个循环迭代停止? (我的理解是lock在这里不合适,因为多个线程在运行时必须能够调用DoWork(),所以没有资源需要互斥)。

如何检查以查看是否我的线程已经很好地停止了,如果没有,就强迫它停止? (我知道Thread.Abort()是不良业障。)

我认为这是一个相当标准的解决方案的常见问题,但我并不真正熟悉正确的命名或.NET方法来调用。

+0

我的答案在您接受答案后发布,这很好。只要确保你将_stopFlag标记为volatile即可。 –

回答

1

StopAllProcesses应循环通过集合中的每个线程并调用.Stop()。那么,理想上你想要的是从每个线程中告知它已停止的方式(在ProcessUntilStoppped结束时设置的标志?)

这应该最终有效,但可能需要一段时间,具体取决于DoWork ()需要。如果所有线程都没有完成(60秒?),则可能必须在其中放置一个计时器,然后中止线程。

1

首先,确保_stopFlag字段标记为volatile。据我了解你的代码,该标志将由主线程设置,但由进程线程读取。通常,这将涉及使用lock语句的同步。但是,对于某些类型,包括bool,您可以简单地将该标志标记为易失性并避免锁定。

DoWork()不会被Thread.Join()“中断”。 Thread.Join()阻止线程调用线程,直到目标线程终止。因此,如果您的主线程调用Thread.Join()并且DoWork()方法需要30分钟才能完成,则主线程将被阻止30分钟。显然,这不是你想要的。

停止线程很简单。 StopAllProcesses()应该迭代集合中的每个线程并调用它们各自的Stop()方法。问题是如何避免主线程从StopAllProcesses()返回,直到所有线程都停止。

一种方法是调用的Thread.Join(Int32)定时版本为每个线程,就像这样:

public void StopAllProcesses() 
{ 
    // Stop the threads. 
    _threads.ForEach(thread => thread.Stop()); 
    // Wait for the threads to terminate. 
    _threads.ForEach(thread => { 
     try { 
      if (!thread.Join(1000)) // 1000 milliseconds = 1 second 
      { 
       thread.Abort(); // try to avoid using this! 
      } 
     } catch { 
      // do something appropriate here 
     } 
    }); 
} 

另一种方式是到Thread.ManualResetEvent对象与每个线程关联。该对象将在每个线程退出ProcessUntilStopped()函数时发出信号。该StopAllProcesses()方法是这样的:

public void StopAllProcesses() 
{ 
    // Create the list of wait handles where Terminated is a public property 
    // of the Process thread class whose type is ManualResetEvent. 
    List<WaitHandle> handles = new List<WaitHandle>(); 
    _threads.ForEach(thread => handles.Add(thread.Terminated)); 
    // Stop the threads. 
    _threads.ForEach(thread => thread.Stop()); 
    // Wait for the threads to terminate. 
    try { 
     if (!WaitHandle.WaitAll(handles.ToArray(), 1000)) 
     { 
      _threads.ForEach(thread => thread.Abort()); 
     } 
    } catch { 
     // do something appropriate here 
    } 
} 

如果使用后者的例子,只要确保进程线程退出ProcessUntilStopped()函数之前Terminated.Set()被调用。