如何阻止线程在特定代码段内终止?

时间:2010-02-10 18:26:17

标签: .net multithreading

我是一个完全线程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方法。

2 个答案:

答案 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()