开发者

How do I stop a thread from terminating within a particular section of code?

I a total threading n00b and I want to figure out a way to shut down a thread first by asking nicely, then by force.

I have a ProcessManager class that starts a bunch of Process class threads:

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;
    }
}

How do I get my thread not to be interrupted while calling DoWork() and instead wait until the next loop iteration to stop? (My understanding is that lock would not be appropriate here because multiple threads have to be able to call DoWork() while they're running, so there's no resource that needs to be mutually exclusiv开发者_运维百科e.)

How do I check to see if my thread has stopped nicely, and if not, force it to stop? (I am aware that Thread.Abort() is bad karma.)

I figure this has to be a common problem with a fairly standard solution, but I'm not really familiar with the right nomenclature or .NET methods to call.


StopAllProcesses should loop through each thread in the collection and call .Stop(). Then, what you ideally want is a way to tell from each thread that it has stopped (a flag you set at the conclusion of ProcessUntilStoppped?)

This should eventually work, but it might take a while depending on how long DoWork() takes. You may have to put a timer in that says, if all threads have not finished after (60 seconds?) then to abort the threads.


First of all, make sure the _stopFlag field is marked as volatile. As I understand your code, the flag will be set by the main thread but read by the Process thread. Normally, this would involve synchronization using the lock statement. However, for certain types, including bool, you can simply mark the flag as volatile and avoid the lock.

DoWork() will not be "interrupted" by Thread.Join(). Thread.Join() blocks the calling thread until the target thread terminates. So if your main thread were to call Thread.Join() and the DoWork() method took 30 minutes to complete, the main thread would be blocked for 30 minutes. Obviously, this is not what you want.

Stopping the threads is straightforward. StopAllProcesses() should iterate over each of the threads in the collection and call their respective Stop() methods. The question is how to avoid the main thread from returning from StopAllProcesses() until all the threads have stopped.

One way would be to invoke the timed version of Thread.Join(Int32) for each thread, like this:

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
        }
    } );
}

Another way would be to associate a Thread.ManualResetEvent object with each thread. The object would be signaled by each thread as it exits the ProcessUntilStopped() function. The StopAllProcesses() method would look like this:

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
    }
}

If this latter example is used, just make sure that Terminated.Set() is called before the Process thread exits the ProcessUntilStopped() function.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜