views:

76

answers:

2

Not sure if my title is worded well, but whatever :)

I have two threads: the main thread with the work that needs to be done, and a worker thread that contains a form with a progress bar and a cancel button. In normal code, it would be the other way around, but I can't do that in this case.

When the user clicks the cancel button, a prompt is displayed asking if he wants to really cancel the work. The problem is that work continues on the main thread. I can get the main thread to stop work and such, but I would like for it to stop doing work when he clicks "Yes" on the prompt.

Example:

// Main thread work starts here    
    t1 = new Thread(new ThreadStart(progressForm_Start));
    t1.Start();

    // Working
    for (i = 0; i <= 10000; i++)
    {
        semaphore.WaitOne();
        if (pBar.Running)
            bgworker_ProgressChanged(i);
        semaphore.Release();
        if (pBar.IsCancelled) break; 
    }

    t1.Abort(); 
// Main thread work ends here

// Start progress bar form in another thread
void progressForm_Start()
{
    pBar.Status("Starting");
    pBar.ShowDialog();
}

I could theoretically include a prompt in the cancelWatch() function, but then I would have to do that everywhere I'm implementing this class.

A: 

I recommend using CancellationTokenSource, which can handle this kind of complex scenario. It's part of the Task Parallel Library but does not actually have to be used with Task objects; it can just as easily be used with old-style Thread objects.

Of course, if you have the time, I'd recommend defining the main thread's work as a Task object (running on the main UI thread by using TaskScheduler.FromCurrentSynchronizationContext).

Note that everything above assumes .NET 4.0. If you're still stuck on the old platform, you'll just have to have a bool cancelled; field protected by a lock or some such thing. Tip: don't call Thread.Abort; it's evil.

Stephen Cleary
Unfortunately, we're stuck on .NET 3.5, because this would be what I needed :( ... Thanks anyways!
Duracell
+1  A: 

I have a couple of quick comments:

  1. Avoid using Thread.Abort() here's why.
  2. Make your thread a background thread: Thread.IsBackground = true (this will automatically exit the thread when your app exits).

Here is a detailed discussion on how to safely stop a thread from running: http://stackoverflow.com/questions/2474945/is-it-safe-to-use-a-boolean-flag-to-stop-a-thread-from-running-in-c

To stop the work on the main thread you'd have to do something like this:

boolean volatile isRunning = true;

static void Main(...)
{
    // ...
    // Working
    for (i = 0; i <= 10000; i++)
    {
        semaphore.WaitOne();
        if (!isRunning) break; // exit if not running
        if (pBar.Running)
            bgworker_ProgressChanged(i);
        semaphore.Release();
    }
    //...
    t1.Interrupt();// make the worker thread catch the exception
}
// 
void cancelButton_Click(object sender, EventArgs e)
{
    isRunning = false; // optimistic stop
    semaphore.Release();
}
Lirik
Alright, thank you. I'll have to try this when I get a chance.
Duracell
@Duracell, yep, let me know how it works out.
Lirik
Sorry I didn't get back to you...we decided to try a different route, but thanks for the answer. It at least pointed us in the right direction :)
Duracell
@Duracell, I'm glad my answer was helpful in some respect :).
Lirik