开发者

How to terminate a worker thread correctly in c#

开发者 https://www.devze.com 2023-01-16 02:42 出处:网络
Problem statement I have a worker thread that basically scans a folder, going into the files within it, and then sleeps for a while. The scanning operation might take 2-3 seconds but not much more. I

Problem statement

I have a worker thread that basically scans a folder, going into the files within it, and then sleeps for a while. The scanning operation might take 2-3 seconds but not much more. I'm looking for a way to stop this thread elegantly.

Clarification: I want to stop the thread while it's sleeping, and not while it's scanning. However, the problem is that I do not know what is the current state of the thread. If it's sleeping I want it to exit immediately. If it's scanning, I want it to exit the moment it tries to block.

Attempts at a solution

At first I was using Sleep and Interrupt. Then I found out that Interrupt doesn't really interrupt the Sleep - it only works when the threads TRIES to go into sleeping.

So I switched to Monitor Wait&Pulse. Then I found out that the Pulse only works when I'm actually in the Wait. So now I have a thread which looks like that:

while (m_shouldRun)
{
    try
    {
        DoSomethingThatTakesSeveralSeconds();
        lock (this)
        {
            Monitor.Wait(this, 5000);
        }
    }
    catch (ThreadInterruptedException)
    {
        m_shouldRun = false;
    }
}

And now I need to craft my Stop function. So I started with:

public void Stop()
{
    m_shouldR开发者_如何学运维un = false;
    lock (this)
    {
        Monitor.Pulse(this);
    }
    thread.Join();
}

But this doesn't work because I may be pulsing while the thread works (while it's not waiting). So I added Interrupt:

public void Stop()
{
    m_shouldRun = false;
    thread.Interrupt();
    lock (this)
    {
        Monitor.Pulse(this);
    }
    thread.Join();
}

Another option is to use:

public void Stop()
{
    m_shouldRun = false;
    while (!thread.Join(1000))
    {
        lock (this)
        {
            Monitor.Pulse(this);
        }
    }
}

The question

What is the preferred method? Is there a third method which is preferable?


The way to stop a thread elegantly is to leave it finish by itself. So inside the worker method you could have a boolean variable which will check whether we want to interrupt. By default it will be set to false and when you set it to true from the main thread it will simply stop the scanning operation by breaking from the processing loop.


Another alternative is to use events:

private ManualResetEvent _event = new ManualResetEvent(false);


public void Run() 
{
 while (true)
 {
    DoSomethingThatTakesSeveralSeconds();
    if (_event.WaitOne(timeout))
      break;
 }
}

public void Stop() 
{
   _event.Set();
   thread.Join();
}


I recommend to keep it simple:

while (m_shouldRun)
{
    DoSomethingThatTakesSeveralSeconds();
    for (int i = 0; i < 5; i++)  // example: 5 seconds sleep
    {
        if (!m_shouldRun)
            break;
        Thread.Sleep(1000);
    }
}

public void Stop()
{
    m_shouldRun = false;
    // maybe thread.Join();
}

This has the following advantages:

  • It smells like busy waiting, but it's not. $NUMBER_OF_SECONDS checks are done during the waiting phase, which is not comparable to the thousands of checks done in real busy waiting.
  • It's simple, which greatly reduces the risk of error in multi-threaded code. All your Stop method needs to do is to set m_shouldRun to false and (maybe) call Thread.Join (if it is necessary for the thread to finish before Stop is left). No synchronization primitives are needed (except for marking m_shouldRun as volatile).


I came up with separately scheduling the task:

using System;
using System.Threading;

namespace ProjectEuler
{
    class Program
    {
        //const double cycleIntervalMilliseconds = 10 * 60 * 1000;
        const double cycleIntervalMilliseconds = 5 * 1000;
        static readonly System.Timers.Timer scanTimer =
            new System.Timers.Timer(cycleIntervalMilliseconds);
        static bool scanningEnabled = true;
        static readonly ManualResetEvent scanFinished =
            new ManualResetEvent(true);

        static void Main(string[] args)
        {
            scanTimer.Elapsed +=
                new System.Timers.ElapsedEventHandler(scanTimer_Elapsed);
            scanTimer.Enabled = true;

            Console.ReadLine();
            scanningEnabled = false;
            scanFinished.WaitOne();
        }

        static void  scanTimer_Elapsed(object sender,
            System.Timers.ElapsedEventArgs e)
        {
            scanFinished.Reset();
            scanTimer.Enabled = false;

            if (scanningEnabled)
            {
                try
                {
                    Console.WriteLine("Processing");
                    Thread.Sleep(5000);
                    Console.WriteLine("Finished");
                }
                finally
                {
                    scanTimer.Enabled = scanningEnabled;
                    scanFinished.Set();
                }
            }
        }
    }
}
0

精彩评论

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