我是一个完全线程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 :(得分:1)
StopAllProcesses应循环遍历集合中的每个线程并调用.Stop()。那么,你理想的想法是从每个线程告诉它已经停止了(你在ProcessUntilStoppped结束时设置的标志?)
这应该最终有效,但可能需要一段时间,具体取决于DoWork()需要多长时间。如果所有线程都没有完成(60秒后?),那么你可能不得不放一个计时器,然后中断线程。
答案 1 :(得分:1)
首先,确保_stopFlag字段标记为volatile。据我理解你的代码,标志将由主线程设置,但由Process线程读取。通常,这将涉及使用lock语句进行同步。但是,对于某些类型,包括bool
,您只需将标记标记为易失性并避免锁定。
Thread.Join()不会“中断”DoWork()。 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
}
}
如果使用后一个示例,只需确保在Process线程退出ProcessUntilStopped()函数之前调用Terminated.Set()
。