0

I've got a Windows Forms Application that does some data fetching from various places. Because of this I've made a thread that fetches and updates the graphical stuff (progressbar, textfields++).

But I'm having some problems quitting it, the thread that is. It goes something like this:

Thread t = new Thread(new ThreadStart(this.Loop))
t.Start();

and the Loop function

void Loop
{
    while(true)
    {
        if( parent window isDisposed )
        break;

        /*
        fetch and update stuff goes in here...
        */

        Thread.Sleep(5000);    
    }
}

Closing the window will make the while break, but it is now disposed??

SethO
  • 2,703
  • 5
  • 28
  • 38
Jason94
  • 13,320
  • 37
  • 106
  • 184

6 Answers6

2

As Johann has suggested you might want to look at BackgroundWorker object. However if this is a learning project and you'd just like to familiarize yourself about threads then by all means!

What I'd suggest is adding a new volatile boolean variable, something like this.

volatile bool CancelationPending = false;
...
Thread T = new Thread(new ThreadStart(method));
CancelationPending = false;
...
void method () {
    while (!CancelationPending)
    {
        /* do stuff*/
    }
}

and on your Form you can add OnClosing event in which you can:

private void OnClosing(object sender, EventArgs e)
{
    CancelationPending = true;
}
David Božjak
  • 16,887
  • 18
  • 67
  • 98
1

One simple way is to define a bool parameter that you use instead of while(true) and a method to set it to false:

bool threadRunning = false;

Thread t = new Thread(new ThreadStart(this.Loop));
threadRunning = true;
t.Start();


void Loop()
{
    while(threadRunning)
    {
        if( parent window isDisposed )
        break;

        /*
        fetch and update stuff goes in here...
        */

        Thread.Sleep(5000);    
    }
}

public void stopThread() 
{ 
    threadRunning = false;
}

Bear in mind though it can still take up to 5 seconds for the thread to stop (or however long your sleep value is set for)

Note: you will need to use the 'InvokeRequired' pattern if the thread updates any controls created by other threads, see: Automating the InvokeRequired code pattern

Community
  • 1
  • 1
Aaron Gage
  • 2,373
  • 1
  • 16
  • 15
1

In such scenarios I often use an AutoResetEvent for waiting inside the loop, paired with a method offering the caller to indicate that the threaded operation should be cancelled. You can make use of the return value in AutoResetEvent.WaitOne and use that as a cancel flag in itself (it returns true if it Set is called, false if it times out):

private AutoResetEvent waitHandle = new AutoResetEvent(false);

void Loop()
{
    while(true)
    {
        /*
        fetch and update stuff goes in here...
        */

        if (waitHandle.WaitOne(5000))
        {
            break;
        }
    }
}

public void Cancel()
{
    waitHandle.Set();
}
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • Very nice... although I personally try to avoid while(true) and for(;;) like the plague... – Aaron Gage Feb 15 '11 at 09:47
  • @Aaron: I agree. I would typically use `do { /* code here */ } while(!waitHandle.WaitOne(5000))`, but I also try to tamper as little as possible with OP code samples so that coding style does not steal attention from the more important details. – Fredrik Mörk Feb 15 '11 at 09:54
  • Its interesting that often we don't realize when we bring a technique from 1 language to another, yet unknown to us is that the class libraries provide for a better way in the new language! (except between .NET languages), ie from Java to C#... – Aaron Gage Feb 15 '11 at 13:21
0

It may be easier for you to use a BackgroundWorker object. It also supports cancellation and has built-in progress reporting capabilities.

Johann Blais
  • 9,389
  • 6
  • 45
  • 65
  • Well, would you also like a drag and drop for loop? – Aaron Gage Feb 15 '11 at 11:31
  • The question has nothing to do with drag & drop. The BW is just a way to run a background task. Like it or not, it is a perfectly valid solution to get rid of the complexity of threading. – Johann Blais Feb 15 '11 at 12:49
0

I would use a static member of the thread to initiate a controled stop of the thread and add in the form's unload something like this:

        stopToDo = true;
        while (todoThread.IsAlive)
        {
            System.Threading.Thread.Sleep(10);
        }

in the thread you have to do something like this:

if(stopToDo)
{
    return;
}
fpdragon
  • 1,867
  • 4
  • 25
  • 36
0

If you just want your new thread to exit when the main thread (probably your GUI thread) exits, you can make your new thread a background thread:

Thread t = new Thread(new ThreadStart(this.Loop));
t.IsBackground = true;
t.Start(); 
Simon Fischer
  • 3,816
  • 3
  • 23
  • 32