Display "Search in progress message" with threading

  • Thread starter Thread starter olig
  • Start date Start date
O

olig

I'm doing some lengthy computation on a separate thread (actually its a
search) and want to display a small window with something like "Search in
progress" and allow the user to cancel the search. I am not sure what would
be the best approach.

I made a small sample, basically I have a form with a start button which
starts the computation in a new thread and display the WaitingForm which is
a simple form with a cancel button to cancel the operation.

The first approach I tried is this:

public class Form1 : System.Windows.Forms.Form
{

//Constructors and .NET generated code removed for simplicity

WaitingForm _waitingForm;
Thread _thread;
AutoResetEvent _waitHandle;

private void button1_Click(object sender, System.EventArgs e)
{
_waitHandle = new AutoResetEvent(false);

_thread = new Thread(new ThreadStart(LongComputation));
_thread.Start();

_waitingForm = new WaitingForm();
_waitingForm.button1.Click += new EventHandler(cancelHandler);
_waitingForm.Show();

_waitHandle.WaitOne();

_waitingForm.Close();
}

private void LongComputation()
{
try
{
for (int i=0; i<100; i++)
{
System.Diagnostics.Debug.WriteLine(i);
Thread.Sleep(100);
}
finally
{
_waitHandle.Set();
}
}
}

private void cancelHandler(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Canceling thread");
_thread.Abort();
}
}

However this doesn't work because the main thread is blocked on
_waitHandle.WaitOne() and does not process messages, so the waiting form is
not correctly displayed and do not respond to events and the user can't
click the cancel button. Maybe I should display this window in a separate
thread?

Then I removed the WaitHandle and I tried this:

public class Form1 : System.Windows.Forms.Form
{

//Constructors and .NET generated code cut for simplicity

WaitingForm _waitingForm;
Thread _thread;

private void button1_Click(object sender, System.EventArgs e)
{
_thread = new Thread(new ThreadStart(LongComputation));
_thread.Start();

_waitingForm = new WaitingForm();
_waitingForm.button1.Click += new EventHandler(cancelHandler);
_waitingForm.ShowDialog();
}

private void LongComputation()
{
try
{
for (int i=0; i<100; i++)
{
System.Diagnostics.Debug.WriteLine(i);
Thread.Sleep(100);
}
}
finally
{
_waitingForm.Close();
}
}

private void cancelHandler(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Canceling thread");
_thread.Abort();
}

}

This works correctlty but I don't feel it is the right way to do things. I
thing that I should use some asynchronous calls and have a callback method
to get signaled when the thread is over.

Any suggestions on the right approach to use?

olig
 
I'd say as a general rule, when you have a process to run in another thread,
make that ThreadStart a function in another class, so that way you still
have some control over it. Then, I'd check for cancel inside that loop.
Something like this (psuedo-code - may not work):

MyThreadClass objTC = new MyThreadClass();
objTC.SetSomeArguments(strThis,strThat,intTheOtherThing);


_thread = new Thread(new ThreadStart(objTC.LongComputation));
_thread.Start();

then, via your cancel button_click - you do this:

objTC.ShouldCancel = false;

public class MyThreadClass
{
public bool ShouldCancel = false;

public void LongComputation()
{
try
{
for (int i=0; i<100; i++)
{
if ( ShouldCancel )
break;
System.Diagnostics.Debug.WriteLine(i);
Thread.Sleep(100);
}
finally
{
_waitHandle.Set();
}
}
}

What do you think? By havnig a thread run as a class, you still have some
external controls over that thread.
 
Drebin said:
I'd say as a general rule, when you have a process to run in another thread,
make that ThreadStart a function in another class, so that way you still
have some control over it. Then, I'd check for cancel inside that loop.
Something like this (psuedo-code - may not work):

What do you think? By havnig a thread run as a class, you still have some
external controls over that thread.

The problem is that in my real application, the long operation isn't in a
loop, so this approach would not work.

Maybe I could pass a delegate to the thread class and the thread would call
this delegate when the operation is over. I will look at the asynchronous
patterns.

olig
 
In that case, I'd have a member function called like bool CheckCancel() and
at good "stopping points" in your long process, you'd periodically check for
cancel.

Blowing out the thread from the main app will likely lead you into trouble
because NOTHING inside your thread is going to get cleaned up properly. You
could be mid-stream writing to the database or to a file.
 
Drebin said:
In that case, I'd have a member function called like bool CheckCancel() and
at good "stopping points" in your long process, you'd periodically check for
cancel.

Blowing out the thread from the main app will likely lead you into trouble
because NOTHING inside your thread is going to get cleaned up properly. You
could be mid-stream writing to the database or to a file.

I don't do any writing in the thread, but I agree that it would be much
better to properly clean up.

Thanks for your suggestions.

Olivier
 
Hi,

Drebin said:
In that case, I'd have a member function called like bool CheckCancel() and
at good "stopping points" in your long process, you'd periodically check for
cancel.

what if there is no way to have stopping points? E.g. if the lengthy
operation performed in the new thread is simply an attempt to connect to a
remote computer (which can take sevral seconds or dozens of seconds)? I
actullay think that there is no proper solution in that case (look at how
outlook handles that... it doesn't: its UI is frozen while it's trying to
connect to mail server, very frustrating) but if somebody has the magical
trick for that...
 
Back
Top