3

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.

duraz0rz
  • 387
  • 1
  • 2
  • 10
  • Why can't you put the form in the main thread and simply use a Swing WorkerThread ? This looks as convoluted as it gets. Also what do you mean by "Normal Code" – Romain Hippeau Jun 18 '10 at 16:15
  • By "Normal code" I meant how I would have thought of doing it at first. I can't put the form in the main thread because I need to be able to actually click the Cancel button anytime to cancel the operation. But I can't use a BackgroundWorker because I don't want asynchronous threads here. The main thread is already a form that displays data and does some stuff in the background. I need a progress bar and a cancel button on top of that, and doing it on the main form is pretty much out of the question. – duraz0rz Jun 18 '10 at 16:34

2 Answers2

5

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: 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();
}
Community
  • 1
  • 1
Kiril
  • 39,672
  • 31
  • 167
  • 226
1

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
  • 437,863
  • 77
  • 675
  • 810