Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken

我是研究僧i 提交于 2019-11-29 08:06:04
Bryan Crosby

You haven't posted enough of your implementation but I would highly recommend a CancellationToken if that is available to you. It's simple enough to use and understand from a maintainability standpoint. You can setup cooperative cancellation as well too if you decide to have more than one worker thread.

If you find yourself in a situation where this thread may block for long periods of time, it's best to setup your architecture so that this doesn't occur. You shouldn't be starting threads that won't play nice when you tell them to stop. If they don't stop when you ask them, the only real way is to tear down the process and let the OS kill them.

Eric Lippert posted a fantastic answer to a somewhat-related question here.

I tend to use a bool flag, a lock object and a Terminate() method, such as:

object locker = new object();
bool do_term = false;

Thread thread = new Thread(ThreadStart(ThreadProc));
thread.Start();

void ThreadProc()
{
    while (true) {
        lock (locker) {
            if (do_term) break;
        }

        ... do work...
    }
}

void Terminate()
{
    lock (locker) {
        do_term = true;
    }
}

Asides from Terminate() all the other fields and methods are private to the "worker" class.

Use a WaitHandle, most preferably a ManualResetEvent. Your best bet is to let whatever is in your loop finish. This is the safest way to accomplish your goal.

ManualResetEvent _stopSignal = new ManualResetEvent(false); // Your "stopper"
ManualResetEvent _exitedSignal = new ManualResetEvent(false);

void DoProcessing() {
    try {
        while (!_stopSignal.WaitOne(0)) {
            DoSomething();
        }
    }
    finally {
        _exitedSignal.Set();
    }
}

void DoSomething() {
    //Some work goes here
}

public void Terminate() {
    _stopSignal.Set();
    _exitedSignal.WaitOne();
}

Then to use it:

Thread thread = new Thread(() => { thing.DoProcessing(); });
thread.Start();

//Some time later...
thing.Terminate();

If you have a particularly long-running process in your "DoSomething" implementation, you may want to call that asynchronously, and provide it with state information. That can get pretty complicated, though -- better to just wait until your process is finished, then exit, if you are able.

There are two situations in which you may find your thread:

  • Processing.
  • Blocking.

In the case where your thread is processing something, you must wait for your thread to finish processing in order for it to safely exit. If it's part of a work loop, then you can use a boolean flag to terminate the loop.

In the case where your thread is blocking, then you need to wake your thread and get it processing again. A thread may be blocking on a ManualResetEvent, a database call, a socket call or whatever else you could block on. In order to wake it up, you must call the Thread.Interrupt() method which will raise a ThreadInterruptedException.

It may look something like this:

private object sync = new object():
private bool running = false;

private void Run()
{
    running = true;
    while(true)
    {
        try
        {
            lock(sync)
            {
                if(!running)
                {
                    break;
                }
            }

            BlockingFunction();
        }
        catch(ThreadInterruptedException)
        {
            break;
        }
    }
}

public void Stop()
{
    lock(sync)
    {
        running = false;
    }
}

And here is how you can use it:

MyRunner r = new MyRunner();
Thread t = new Thread(()=>
{
    r.Run();
});

t.IsBackground = true;
t.Start();

// To stop the thread
r.Stop();

// Interrupt the thread if it's in a blocking state
t.Interrupt();

// Wait for the thread to exit
t.Join();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!