How to Stop Background Thread that's looping?

Hi,

I appologize in advance for my limited threading knowledge. I've programmed quite a bit, but never used threads before. I know the general idea behind them though and I've read most of the Threading documentation in the MSDN library. But if I have the wrong programming model in my head, by all means correct me.

My application is this (I'm using C#.NET), I have hardware that timestamps signals coming into it and a digital IO card to continually grab the data from the hardware. I want to create a background thread to do this data collection continuously in the background. The reason I want it continuous is that the hardware (basically a counter) eventually rolls over, so my software keeps track of this rollover so that all the signals coming in have the correct absolute time. So I want this data collection to be going on continuously in the background. Sounds like a perfect thing for threads. Now I've defined a callback for the class that runs in this background thread, I'm basically going to make it call back into my main program after a certain period of time, or when a certain amount of data is collected and copy that data to an array of doubles in the main program. This array will be overwritten each time the callback gets called. That's fine, the absolute timing information will taken care of, but my main program will be able to choose when to use the data, unused data will get overwritten and lost which is fine (the important thing is that the absolute timing information is always correct).

So first thing, I want the method that's called in the thread to basically just enter a while loop collecting data until the main program tells it to stop. So I define a _taskRunning variable and set it to true when I start the measurement loop, it keeps going until this changes. So now I define another method in my ThreadClass that sets this to false to stop the measurement. I've tried it out and it works, the measurement thread maybe keeps going through the loop once more, but it quits when I update the _taskRunning bool. Now is this the proper way to do it, or have I just broken a bunch of proper coding rules If I have, what's the correct way to do it Remember, I want to start this background thread running to collect measurement data, periodically have it callback to the main program and update an array variable there, run indefinitely in the background until the user clicks stop in the main program. How do I communicate the stop action to the background thread

Second question, in that callback that updates the data array in the main program, I know I have to worry about the main program and the Thread working with that variable at the same time. Is this where I'm supposed to use the Monitor class And if so how do I go about using it

Thanks for any help you can offer,
Chris Erven

PS. Since I know this post might be a bit convoluted, here's the pertinent code fragments

// Main Program Class
// Start the Channel Counts Measurement
private void StartMeasurement()
{
// Setup NI objects, happens in the Measurement object, pass the required input to // the Measurement Thread object
_measureThreadWorker = new MeasurementThread(_physicalChannel, _clockFreq, _numSamples, _timeout, _timeTagRes,
_baseFileName, new MeasurementThreadCallback(DataUpdateCallback));

// Set the MeasurementThread to running
_measureThreadWorker.StartTask();
// Create a thread to execute the task, and then start the thread
_measureThread = new Thread(new ThreadStart(_measureThreadWorker.Measurement));
_measureThread.Start();
MessageBox.Show("ChannelRatesUtilityForm.StartMeasurement measureThread started");
}

// Stop the Channel Counts Measurement
private void StopMeasurement()
{
MessageBox.Show("ChannelRatesUtilityForm.StopMeasurement");

// Stop the measurement thread Thread.Join();
_measureThreadWorker.StopTask();
_measureThread.Join();
MessageBox.Show("ChannelRatesUtilityForm.StartMeasurement measureThread ended, main thread ends.");
}

// Callback method from the measurement thread every time a block of data is filled
// The callback method must match the signature of the callback delegate
public static void DataUpdateCallback(int count)
{
MessageBox.Show("DataUpdateCallback " + count);
}

// Thread Worker Class
public void Measurement()
{
_taskRunning = true;
int i = 0;
while (_taskRunning)
{
if (_measureCallback != null)
{
Thread.Sleep(3000);
_measureCallback(i);
i++;
}
}
}

// Start the Measurement task
public void StartTask()
{
MessageBox.Show("MeasurementThread.StartTask");
_taskRunning = true;
}

// Stop the Measurement task
public void StopTask()
{
MessageBox.Show("MeasurementThread.StopTask");
_taskRunning = false;
}


Answer this question

How to Stop Background Thread that's looping?

  • Dave Salzman

    I have a similar situation...Maybe someone can let me know if this seems reasonable or maybe there is a better way to do this

    I have a little application that communicates with a camera that might need to wait up to 5-10 seconds trying to capture an image because another application might still have the camera open....I don't want to get into that...but anyways...

    I start up a background worker that just keeps banging away at the camera until it either gets the picture, or a certain amount of time elapses.  The way I am handling the timeout is that before I start the background worker, I start a timer on the form/main thread that waits for a predetermined amount of time.

    Once that timespan has elapsed, I do something like this:

    If picWorker.isBusy Then
      picTimer.Stop()
      picWorker.cancelAsync()
    End If

    In my main DoWork loop, I just keep checking to see if the image is nothing and the CancellationPending is false.

    I have a try..except structure in the loop that tries to take a picture. It just swallows the exception if it can't take a picture.  I then just handle the exception of not being able to take the picture once my timeout has fired.


  • tony_qy

    You seem to be on the right track with everything. Here are some suggestions that may solve your problem:

    First, move all the code for starting and stopping the thread to the MeasurementThread class. It is easier to manage the whole thread process if the class is self-contained.
    Second, change the Thread.Join() call to use a timeout value, so it does not block indefinitely.
    Third, add an Abort() method to the class which calls Thread.Abort(), in case setting the flag value does not achieve the desired result.
    Fourth, modify the Thread.Sleep(3000) call to a value far less in order to allow the thread to be more responsive to the change in the execution flag.
    Finally, make sure the variable access is thread-safe - see the lock keyword and the ReaderWriterLock class.

    Here is an example of what I am talking about:

    public class MeasurementThread
    {
    private Thread _Thread;
    private ThreadStart _FunctionPointer;
    private bool _taskRunning;
    private MeasurementThreadCallback _callBackPointer;
    private ReaderWriterLock _Lock;

    ... any other variables here...

    public MeasurementThread(... parameters ...)
    {
    _Lock = new ReaderWriterLock();
    _FunctionPointer = new ThreadStart(Measurement);
    ... any other construction here ...
    }

    //Property to tell if the thread is running.
    public bool IsExecuting
    {
    get
    {
    bool returnValue = false; //Return value.

    lock(this)
    {
    _Lock.AcquireReaderLock(10000);
    returnValue = _Thread.IsAlive;
    _Lock.ReleaseLock();
    }
    return returnValue;
    }

    }

    //Begins the thread execution.
    public void StartTask()
    {
    MessageBox.Show("MeasurementThread.StartTask");
    _taskRunning = true;

    //Create and launch thread.
    _Thread = new Thread(_FunctionPointer);
    _Thread.IsBackground = true;
    _Thread.Start();
    }

    //Ends the thread execution - one way or another
    public void StopTask()
    {
    MessageBox.Show("MeasurementThread.StartTask");
    if (_Thread != null)
    {
    _taskRunning = false;
    _Thread.Join(12000);
    if (_Thread.IsAlive)
    Abort();
    _Thread = null;
    }
    _taskRunning = false;
    }

    //Forces the thread to terminate.
    public void Abort()
    {
    if (_Thread != null)
    {
    _Thread.Abort();
    }
    _Thread = null;
    }

    public void Measurement()
    {
    _taskRunning = true;
    int i = 0;
    while (_taskRunning)
    {
    if (_measureCallback != null)
    {
    Thread.Sleep(500);
    lock(this)
    {
    //If an error occurs in the callback method
    //implementation and is not caught, the thread will
    // be terminated abruptly.
    try
    {
    _measureCallback(i);
    }
    catch (Exception)
    {
    }
    }
    i++;
    }
    }
    }
    }

    --
    Sam Jones
    Adaptive Intelligence
    http://www.adaptiveintelligence.net



  • How to Stop Background Thread that's looping?