3

This is the first time I have tried to use threads in an application. I know this question has been asked before but of the solutions I have looked at I can't see how to apply them in my situation.

I have a program where a datagridview is refreshed on a timer every 60 seconds. The data comes from a SQL database. This timer also starts a worker thread to look for a particular Bluetooth device and update the database based on its findings in the background. The Bluetooth lookup is particularly slow which is why I put it in a worker thread.

My problem is, sometimes a new worker thread starts before the previous one has finished. At least that is the only logical explanation for the errors I'm getting. The big give away is a file locked error when the only thing that can be locking the file is another worker thread from the same application.

Here is the code I'm using to start the background thread.

private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

    // If the user is not on a Remote desktop connection
    if (!remoteDesktopUser)
    {

        // Run the Bluetooth Search in a worker thread
        Thread thread = new Thread(new ThreadStart(this.checkProximity));
        thread.IsBackground = true;
        thread.Start();
    }

    // Load User Data from the DB and display on the screen
    loadUserData();
}

It would seem that the solution is to use thread.IsAlive() but I haven't been able to find a good example. It seems odd to try and check for the existence of a thread when I have just created a new one with "Thread thread = new Thread()"

Obviously I'm missing something. I'm using Visual Studio 2008. Thanks for any ideas David

UPDATE

Based on the proposed solution by krw12572 below I tried this...

I changed the != to == because I still want to run the loadUserData() method in the primary thread every time through.

In the editor I get a green underline on "_bluetoothSearchThread" telling me the field is never assigned and will always have the value NULL.

At run time I get an "Object Reference Not Set to An instance of an object" error on this line.

if (_bluetoothSearchThread == null && _bluetoothSearchThread.IsAlive)

How does this value get assigned?

    private Thread _bluetoothSearchThread;
    private void timerScreenRefresh_Tick(object sender, EventArgs e)
    {
        if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

        // Check if Worker Thread is already running.
        if (_bluetoothSearchThread == null && _bluetoothSearchThread.IsAlive)
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Previous Worker Thread not running");

            // If the user is not on a Remote desktop connection
            if (!remoteDesktopUser)
            {
                // Check if the users mobile phone is within range
                // Run the Bluetooth Search in a worker thread

                Thread thread = new Thread(new ThreadStart(this.checkProximity));
                thread.IsBackground = true;
                thread.Start();
            }
        }
        else 
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Worker Thread still running don't start another one");
        }

        // Load User Data from the DB and display on the screen
        loadUserData();
    }

Update 2

OK I think I have figured it out. I changed the != Null back to the way it was and turned the IF and Else Around the other way.

Then I had to use my brain a bit and changed "thread" to "_bluetoothSearchThread"

Now the code compiles and runs. Now I just need to test it by triggering the conditions that cause the file lock error to see if I have actually fixed the original problem. If it works I'll mark krw12572 answer as correct.

Update 2.5 I also had to move this line so it doesn't create a new instance too soon

_bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));

So this is the working solution.

    private Thread _bluetoothSearchThread;
    private void timerScreenRefresh_Tick(object sender, EventArgs e)
    {
        if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");


        // Check if Worker Thread is already running.
        if (_bluetoothSearchThread != null && _bluetoothSearchThread.IsAlive)
        {
            // Thread is still running.  Just log it and move on.
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "******** Worker Thread still running don't start another one *********");
        }
        else 
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Previous Worker Thread not running");

            // If the user is not on a Remote desktop connection
            if (!remoteDesktopUser)
            {
                // Check if the users mobile phone is within range
                // Run the Bluetooth Search in a worker thread
                _bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));
                _bluetoothSearchThread.IsBackground = true;
                _bluetoothSearchThread.Start();
            }
        }

        // Load User Data from the DB and display on the screen
        loadUserData();
    }
David P
  • 411
  • 7
  • 21
  • Here your result: http://stackoverflow.com/questions/12949024/detecting-a-thread-is-already-running-in-c-sharp-net – BALA s Nov 30 '16 at 06:08
  • @BALA I did see that solution before. It seemed overly complex and I didn't really understand it. I'll try and blindly follow it tomorrow and see if something workable comes out. – David P Nov 30 '16 at 06:27
  • Possible duplicate of [How to check if Thread finished execution](http://stackoverflow.com/questions/2773479/how-to-check-if-thread-finished-execution) – Ashwin Gupta Nov 30 '16 at 06:30
  • @AshwinGupta I have read that post and it suggests a few different options but does not explain how to actually use them. At least not that I can understand. – David P Dec 01 '16 at 00:19

3 Answers3

4

EDIT:

after researching, using Thread.IsAlive in your situtation is not a safe way to do it.

You should use Threa.Join()

Documentation:

Blocks the calling thread until the thread represented by this instance terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.

Example:

while(!currentThread.Join(0)) //should specify the time if you dont want it to be blocking.
{
    ///thread is ongoing
}
Console.WriteLine("while loop has breaked! so the thread is finished!");
Lorence Hernandez
  • 1,189
  • 12
  • 23
  • How would I use Thread.IsAlive in my example code. Could you give me some ideas please? – David P Nov 30 '16 at 06:03
  • @LorenceHernandez I don't want to block the main thread If the previous worker thread is still running. Just don't start another worker thread and keep processing in the main thread. The main thread will check again for the existence of the worker thread on the next timer tick. – David P Dec 01 '16 at 00:24
4

If you want only one thread running at a time, then you can create a field to store Thread instance. Using that thread instance you can check if it's already running or not by using _threadInstance.IsAlive.

private Thread _bluetoothSearchThread;
private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    if(_bluetoothSearchThread != null && _bluetoothSearchThread.IsAlive) 
        return;        //It means one thread is already performing the search operation.

    if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

    // If the user is not on a Remote desktop connection
    if (!remoteDesktopUser)
    {

        // Run the Bluetooth Search in a worker thread
        _bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));
        _bluetoothSearchThread.IsBackground = true;
        _bluetoothSearchThread.Start();
    }

    // Load User Data from the DB and display on the screen
    loadUserData();
}
Kamalesh Wankhede
  • 1,475
  • 16
  • 37
  • I tried that. It had some problems. I updated my question to include your proposed solution and the error messages. Thanks David – David P Dec 01 '16 at 00:06
  • I updated my answer. Instead of creating Thread thread = new Thread(..), use newly created field for thread instance. _bluetoothSearchThread = new Thread(...) – Kamalesh Wankhede Dec 01 '16 at 02:01
0

You might want to use Task instead of Thread.

// Create dummy task
Task task = Task.Run(() => { });

private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    ...
    // add continuation to current task
    task.ContinueWith(t => checkProximity);
    ...
}

The new task will be executed only after the previous one.

However, if the tasks do not have time to run during the Tick, they will accumulate in the queue.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • Hi Alexander, I was thinking of tasks as an alternative although there is no point in me queuing up sub tasks. I now understand what krw12572 is suggesting and my solutions above now works. To test I set my timer tick to 10 seconds and I could see from my logs it was opening and closing threads all over the place. With the above code in place in is nice and orderly. Thread 11 Starts and nothing else starts until Thread 11 is finished. So any other errors I get now are not related to overlapping threads. Thanks for your ideas. David – David P Dec 01 '16 at 03:08