atwellpub
atwellpub

Reputation: 6020

Suspend a child thread from a button on the main UI thread

I've found this topic, How to suspend a thread by its name from the main thread?, but no satisfactory answer for what I'm trying to achieve.

I'm using threading and the WatiN class to perform events on two browsers in the same windows form at the same time.

I would like to, from the main UI thread, press a pause button available within one of the browsers that, through deriving the control name of the browser the pause button was pressed on, use that' name to figure out which sub-thread is associated with it's running logic, and pause that running logic until the play button is pressed.

Now today, we are so accomplished in terms of code and technology, there should be a way to do this.

What do you think?

Researching Ideas:

Pragmatically create ManualResetEvent and name it, use the UI pause button to grab the open browser control name, which is similiarly named after the child thread and browser control name (such a browser_5 & thread_5) to somehow target in on the MRE in the child thread, and close the gate to pause the logic. (But can this be done on child thread from the main UI thread?)

Upvotes: 1

Views: 742

Answers (2)

Merlyn Morgan-Graham
Merlyn Morgan-Graham

Reputation: 59131

Don't use thread.Suspend

At first blush, it seems you could use thread.Suspend() to pause it and thread.Resume() to unpause it. But this is not a very good idea. See the MSDN article for thread.Suspend for why you should never use it unless you intend to terminate the AppDomain for that thread.

Do not use the Suspend and Resume methods to synchronize the activities of threads. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily.

A sub-loop would work, but isn't perfect

It isn't the best option, but you could use a similar technique to the one described in that question you linked.

Instead of exiting the loop when a stop button is pressed, have it enter and wait inside a sub-loop while paused. Do a Thread.Sleep in that sub-loop to keep the CPU from pegging.

This isn't the most efficient code possible, because it keeps the thread running, and hangs for another 100ms when resuming.

public class YourForm : Form
{
  private volatile bool _pause = false;

  private void StartButton_Click(object sender, EventArgs e)
  {
    var thread = new Thread(
      () =>
      {
        while (...)
        {
          // Periodically poll the _pause flag.
          while (_pause)
          {
            // Now that we're paused, wait until we're unpaused
            // before proceeding further in the outer loop
            Thread.Sleep(100);
          }

          // Todo: The rest of the processing here
        }
      });
    thread.Start();
  }

  private void PauseButton_Click(object sender, EventArgs e)
  {
    _pause = !_pause; // Toggle
  }
}

Use thread synchronization

The best option is to use one of the various thread synchronization structures, like ManualResetEvent. Pausing threads is exactly what they're designed for. They're very efficient because they are implemented with a mutex.

public class YourForm : Form
{
  private volatile bool _pause = false;
  private static ManualResetEvent mre = new ManualResetEvent(true);

  private void StartButton_Click(object sender, EventArgs e)
  {
    var thread = new Thread(ThreadImplementation);
    thread.Start();
  }

  private void PauseButton_Click(object sender, EventArgs e)
  {
    _pause = !_pause;

    if(_pause)
    {
      mre.Reset();
    }
    else
    {
      mre.Set();
    }
  }

  private void ThreadImplementation()
  {
    while (...)
    {
      // Periodically wait on the event
      mre.WaitOne();

      // Todo: The rest of the processing here
    }
  }
}

Upvotes: 3

SLaks
SLaks

Reputation: 887777

Your ManualResetEvent idea is exactly correct.

Make the child threads WaitOne() on the event between each step.
To pause the request, call Reset(); to unpause, call Set().

If the event is set, WaitOne() will return immediately.

This will be much more efficient than repeated sleeps.

I suspect that a ManualResetEventSlim would be slightly faster.

Upvotes: 1

Related Questions