Convert asynchronous to synchronous

C

cj_junktrap

Hi

I have a method which I want to present as a synchronous call - you
pass in your parameters, and the result as a return value. But within
that method, I need to spin off a thread, wait for it to finish doing
some stuff, and return a value to the calling method. The problem is
that my method may be called on the gui thread (since it's out of my
control where my method is called from), and the gui will become
unresponsive while my method is running if I wait for the thread to
finish using Thread.Join. So what I'm doing at the moment is a loop
like this:

while (Thread.IsAlive)
{
Thread.Sleep(100);
Application.DoEvents();
}

Now I know that using Application.DoEvents like that is horrible, but I
can't see any other way to wait for the thread to finish before
returning the value to the calling method. Sure, I could pass the
thread a delegate to call when it's finished, but I want the whole
process to appear to be synchronous to the calling method. Is there a
better way to accomplish this?
 
M

Markus Stoeger

Now I know that using Application.DoEvents like that is horrible, but I
can't see any other way to wait for the thread to finish before
returning the value to the calling method. Sure, I could pass the
thread a delegate to call when it's finished, but I want the whole
process to appear to be synchronous to the calling method. Is there a
better way to accomplish this?

Maybe you could open a modal dialog box with a progress bar on it or
something similar? Otherwise I'd probably make the call asynchronous and
add a callback..

Max
 
C

Carl Daniel [VC++ MVP]

Hi

I have a method which I want to present as a synchronous call - you
pass in your parameters, and the result as a return value. But within
that method, I need to spin off a thread, wait for it to finish doing
some stuff, and return a value to the calling method. The problem is
that my method may be called on the gui thread (since it's out of my
control where my method is called from), and the gui will become
unresponsive while my method is running if I wait for the thread to
finish using Thread.Join. So what I'm doing at the moment is a loop
like this:

while (Thread.IsAlive)
{
Thread.Sleep(100);
Application.DoEvents();
}

Now I know that using Application.DoEvents like that is horrible, but
I can't see any other way to wait for the thread to finish before
returning the value to the calling method. Sure, I could pass the
thread a delegate to call when it's finished, but I want the whole
process to appear to be synchronous to the calling method. Is there a
better way to accomplish this?

You're best off not trying to present a synchronous interface to a long
running function if it may be called from the GUI thread. Using
Application.DoEvents as you've shown does simulate the effect, but only if
it is in fact called from the GUI thread, and at the cost of having the GUI
thread spin, needlessly burning CPU cycles. The problem is, there's no way
in WinForms to start a new (non-polling) message loop and cause that message
loop to exit when a desired event occurs. So, in this case, what you should
do in your function is nothing - just make it synchronous. The GUI needs to
take care of itself - which means never calling your long-running function
on the GUI thread.

So what's the GUI to do when it needs to call a long-running function? Use
a worker thread. This is the technique that I use: I always have the "main
form" of the application available throughout the app - your choice how to
make it available (there are lots of ways).

Within the main form (called "Shell" in the code below), I'll expose a
public function like this:

public void RunBackgroundTask(object s, WaitCallback item, WaitCallback
cleanup)
{
// do any app-specific "lockout" work here
// ...

ThreadPool.QueueUserWorkItem(
delegate(object state)
{
try
{
item(state);
}
catch (Exception e)
{
// do whatever you think appropriate to handle/log the exception
Trace.WriteLine(string.Format("Internal error {0}:{1} [{2}]",
e.GetType().FullName, e.Message, e.StackTrace));
}
this.After_BackgroundItem(s, cleanup);
},
s
);
}

private delegate void CallbackEventHandler(object state, WaitCallback
callback);

private void After_BackgroundItem(object state, WaitCallback cleanup)
{
if (InvokeRequired)
{
object[] oa = new object[] { state, cleanup };
Invoke(new CallbackEventHandler(After_BackgroundItem),oa);
}
else
{
// do any app-specific "unlock" work here
// ...

if (null != cleanup)
cleanup(state);
}
}

Then, where I want to do some async work (directly or indirectly, this will
be called by a message handler called by the main window message loop):

Shell.RunBackgroundTask(
this,
// This part is run by a worker thread
delegate(object state)
{
// background task code here
},
// This part is run in the UI thread after the task completes
delegate(object state)
{
// UI cleanup code here
}
);

The C# 2.0 anonymous delegates let you easily partition the background
worker code from the UI code without having to create a bunch of single-use
functions. This keeps all of the code related to the function close
together and (I think) makes it easier to understand - once you accept that
the first anonymous block runs in a worker thread and the second runs in the
GUI thread. Note that within the anonymous blocks of code, all local and
member variables of the enclosing function and class are accessible, thanks
to the closures that the C# compiler creates for you.

You could also use the BackgroundWorker component that's new in WinForms
2.0. Of course, if you're stil on 1.x, you have little choice but to
directly use ThreadPool and/or asynchronous delegate invocations (which
internally use the thread pool).

-cd
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top