Threading in a form...

C

ChrisM

Hi,

I'm trying to get my head round Async. procedure calls.

I have the following code:

delegate double GetLabourWIPDelegate(DateTime reqDate);
....
GetLabourWIPDelegate getLabourWIPDelegate = new
GetLabourWIPDelegate(GetLabourWIPOnDate);
AsyncCallback ac = new AsyncCallback(DoResult);
IAsyncResult ar = getLabourWIPDelegate.BeginInvoke(dteToDate.Value,ac,null);
....
private void DoResult(IAsyncResult ar)
{
GetLabourWIPDelegate getLabourWIPDelegate =
(GetLabourWIPDelegate)((AsyncResult)ar).AsyncDelegate;
double result = getLabourWIPDelegate.EndInvoke(ar);
lblWIPLabourTo.Text = result.ToString("###,###,###,##0.00");
}

This seems to work, it starts a new thread that async. does the calculation
on the date in dteToDate ( calls GetLabourWIPOnDate), then calls DoResult on
completion which sets the label on the form to the result of the
calculation. Is is correct though?

My other question is, what happens if I want to do another calculation at
the same time. I will be calling the same calculation method, but using a
different date value, and putting the result into a different label. How
would I alter my code to do this?

Hope this makes sense, if not, please ask me to clarify anything.

Thanks,

Chris.
 
D

Dave Sexton

Hi Chris,

I usually pass in the delegate reference as the state argument so you can avoid the cast to AsyncResult.

You might want to encapsulate this functionality in a class. You could create a GetLabourWIPAsync method and an event that is
raised when the asynchronous operation has completed. Or, you can create BeginGetLabourWIP and EndGetLabourWIP methods. See the
links below for more info.

In the .NET 2.0 framework you can use a BackgroundWorker component instead of a delegate to provide the same functionality, along
with some extra functionality such as a progress callback and optional cancellation. BackgroundWorker can be used in the designer.

Async design pattern on MSDN:
http://msdn.microsoft.com/library/d...ml/cpconasynchronousdesignpatternoverview.asp

Event-based async pattern on MSDN:
http://msdn2.microsoft.com/en-us/library/wewwczdw.aspx

Best practices for event-based async pattern on MSDN:
http://msdn2.microsoft.com/en-us/library/ms228974.aspx
 
B

Brian Gideon

Chris,

First, you cannot access controls from the callback method because it
is running on a thread other than the main UI thread. Forms and
controls must be accessed only from the main UI thread. You'll need to
use the Invoke method to marshal a delegate onto the thread hosting the
label you're wanting to modify.

To execute GetLabourWIPOnDate multiple times simultaneously just keep
calling BeginInvoke on the delegate. If you need to each one to modify
a different label when complete then just pass the label as the state
parameter.

The following example demonstrates how to do this. Hopefully it
compiles...I wrote it without testing.

void ExampleMethod()
{
GetLabourWIPDelegate delegate =
new GetLabourWIPDelegate(GetLabourWIPOnDate);

AsyncCallback callback = new AsyncCallback(DoResult);

delegate.BeginInvoke(date1, callback, label1);
delegate.BeginInvoke(date2, callback, label2);
// ...
delegate.BeginInvoke(dateN, callback, labelN);
}

void DoResult(IAsyncResult ar)
{
Label label = (Label)ar.AsyncState;
if (label.InvokeRequired)
{
// Rerun this method on the UI thread.
Delegate method = new AsyncCallback(DoResult);
object[] parameters = { ar };
label.Invoke(method, parameters);
}
else
{
// We're already on the UI thread!
GetLabourWIPDelegate delegate =
(GetLabourWIPDelegate)((AsyncResult)ar).AsyncDelegate;
double result = delegate.EndInvoke(ar);
label.Text = result.ToString("###,###,###,##0.00");
}
}

Brian
 
C

ChrisM

Brian,

Thank you very much. Exactly what I wanted to know, and some sample code as
well! Usenet answers just don't get much more helpful.
It didn't occur to me that the call back method was being called by the new
thread. Obvious now you've pointed it out, but didn't think at the time. I
can see that programming threads can be somthing of a minefield...

PS.
Only one thing wrong with your code ;-)) you called your Delegate
'delegate' which is obvioulsy a reserved word, and had the compiler whining
a little bit.

PPS

I assume that in calling the 'GetLaboutWIPOnDate' method, each thread gets
its own set of local variables etc, and is 'isolated' from all the other
threads...? I guess the only danger would be if this method referenced (or
rather actually changed) any global values, in which case there would be
some contention?

One more question (sorry!) If the method that is called by the threads,
makes a call to a static method in an object ( eg
MyObject.MyStaticMethod(someValue) ) , is this 'thread safe'? Or will it
cause problems? If it makes a difference, in my case, the static method in
question is a very simple one, it just performs a straightforward
transformation on the value passed in.

Do you have a link to a good introduction to all this stuff that would be
suitable to a newbie thread programmer like myself?

Regards,

Chris.

Brian Gideon said:
Chris,

First, you cannot access controls from the callback method because it
is running on a thread other than the main UI thread. Forms and
controls must be accessed only from the main UI thread. You'll need to
use the Invoke method to marshal a delegate onto the thread hosting the
label you're wanting to modify.

To execute GetLabourWIPOnDate multiple times simultaneously just keep
calling BeginInvoke on the delegate. If you need to each one to modify
a different label when complete then just pass the label as the state
parameter.

The following example demonstrates how to do this. Hopefully it
compiles...I wrote it without testing.

void ExampleMethod()
{
GetLabourWIPDelegate delegate =
new GetLabourWIPDelegate(GetLabourWIPOnDate);

AsyncCallback callback = new AsyncCallback(DoResult);

delegate.BeginInvoke(date1, callback, label1);
delegate.BeginInvoke(date2, callback, label2);
// ...
delegate.BeginInvoke(dateN, callback, labelN);
}

void DoResult(IAsyncResult ar)
{
Label label = (Label)ar.AsyncState;
if (label.InvokeRequired)
{
// Rerun this method on the UI thread.
Delegate method = new AsyncCallback(DoResult);
object[] parameters = { ar };
label.Invoke(method, parameters);
}
else
{
// We're already on the UI thread!
GetLabourWIPDelegate delegate =
(GetLabourWIPDelegate)((AsyncResult)ar).AsyncDelegate;
double result = delegate.EndInvoke(ar);
label.Text = result.ToString("###,###,###,##0.00");
}
}

Brian
Hi,

I'm trying to get my head round Async. procedure calls.

I have the following code:

delegate double GetLabourWIPDelegate(DateTime reqDate);
...
GetLabourWIPDelegate getLabourWIPDelegate = new
GetLabourWIPDelegate(GetLabourWIPOnDate);
AsyncCallback ac = new AsyncCallback(DoResult);
IAsyncResult ar =
getLabourWIPDelegate.BeginInvoke(dteToDate.Value,ac,null);
...
private void DoResult(IAsyncResult ar)
{
GetLabourWIPDelegate getLabourWIPDelegate =
(GetLabourWIPDelegate)((AsyncResult)ar).AsyncDelegate;
double result = getLabourWIPDelegate.EndInvoke(ar);
lblWIPLabourTo.Text = result.ToString("###,###,###,##0.00");
}

This seems to work, it starts a new thread that async. does the
calculation
on the date in dteToDate ( calls GetLabourWIPOnDate), then calls DoResult
on
completion which sets the label on the form to the result of the
calculation. Is is correct though?

My other question is, what happens if I want to do another calculation at
the same time. I will be calling the same calculation method, but using a
different date value, and putting the result into a different label. How
would I alter my code to do this?

Hope this makes sense, if not, please ask me to clarify anything.

Thanks,

Chris.
 
B

Brian Gideon

ChrisM said:
Brian,

Thank you very much. Exactly what I wanted to know, and some sample code as
well! Usenet answers just don't get much more helpful.
It didn't occur to me that the call back method was being called by the new
thread. Obvious now you've pointed it out, but didn't think at the time. I
can see that programming threads can be somthing of a minefield...

Well, actually asynchronous delegates run on a thread in the ThreadPool
so if you get a lot of them started then it's likely that at least a
few would be dispatched to the same thread. In other words, if you
kick off 100 of them there won't be 100 new threads created. Just to
stir the pot some more you could run delegates asynchronously using the
ThreadPool.QueueUserWorkItem method as well.

And yes, multithreaded programming is *insanely* difficult. Even
experts get tripped up frequently.
PS.
Only one thing wrong with your code ;-)) you called your Delegate
'delegate' which is obvioulsy a reserved word, and had the compiler whining
a little bit.

Oops :)
PPS

I assume that in calling the 'GetLaboutWIPOnDate' method, each thread gets
its own set of local variables etc, and is 'isolated' from all the other
threads...? I guess the only danger would be if this method referenced (or
rather actually changed) any global values, in which case there would be
some contention?

That is correct. Local variables are allocated on the stack which
means each thread will get its own copy. Like you said, be careful
when accessing class level or instance or static variables. You'll
need to synchronize access to prevent race conditions.
One more question (sorry!) If the method that is called by the threads,
makes a call to a static method in an object ( eg
MyObject.MyStaticMethod(someValue) ) , is this 'thread safe'? Or will it
cause problems? If it makes a difference, in my case, the static method in
question is a very simple one, it just performs a straightforward
transformation on the value passed in.

Since it's only modifying a parameter then yes, it's probably
thread-safe. But, if that parameter reference the same object across
all running instances of the method then it won't be. That didn't
sound like the case though.
Do you have a link to a good introduction to all this stuff that would be
suitable to a newbie thread programmer like myself?

Yes, I do. The following is probably the most cited article.

http://www.yoda.arachsys.com/csharp/threads/

But, this one appears to be well written too.

http://www.albahari.com/threading/
 
B

Brian Gideon

ChrisM said:
Brian,

Thank you very much. Exactly what I wanted to know, and some sample code as
well! Usenet answers just don't get much more helpful.
It didn't occur to me that the call back method was being called by the new
thread. Obvious now you've pointed it out, but didn't think at the time. I
can see that programming threads can be somthing of a minefield...

Well, actually asynchronous delegates run on a thread in the ThreadPool
so if you get a lot of them started then it's likely that at least a
few would be dispatched to the same thread. In other words, if you
kick off 100 of them there won't be 100 new threads created. Just to
stir the pot some more you could run delegates asynchronously using the
ThreadPool.QueueUserWorkItem method as well.

And yes, multithreaded programming is *insanely* difficult. Even
experts get tripped up frequently.
PS.
Only one thing wrong with your code ;-)) you called your Delegate
'delegate' which is obvioulsy a reserved word, and had the compiler whining
a little bit.

Oops :)
PPS

I assume that in calling the 'GetLaboutWIPOnDate' method, each thread gets
its own set of local variables etc, and is 'isolated' from all the other
threads...? I guess the only danger would be if this method referenced (or
rather actually changed) any global values, in which case there would be
some contention?

That is correct. Local variables are allocated on the stack which
means each thread will get its own copy. Like you said, be careful
when accessing class level or instance or static variables. You'll
need to synchronize access to prevent race conditions.
One more question (sorry!) If the method that is called by the threads,
makes a call to a static method in an object ( eg
MyObject.MyStaticMethod(someValue) ) , is this 'thread safe'? Or will it
cause problems? If it makes a difference, in my case, the static method in
question is a very simple one, it just performs a straightforward
transformation on the value passed in.

Since it's only modifying a parameter then yes, it's probably
thread-safe. But, if that parameter reference the same object across
all running instances of the method then it won't be. That didn't
sound like the case though.
Do you have a link to a good introduction to all this stuff that would be
suitable to a newbie thread programmer like myself?

Yes, I do. The following is probably the most cited article.

http://www.yoda.arachsys.com/csharp/threads/

But, this one appears to be well written too.

http://www.albahari.com/threading/
 
C

ChrisM

Hi Dave,

Thanks for your answer, some useful tips and ideas in there!

I'm stuck with .NET 1.1 for now, so can't use any of the new stuff in 2.0
:-(

Cheers,

Chris.
 
D

Dave Sexton

Hi Chris,

That's too bad. Just FYI, the async pattern and event-based async pattern info, and the recommendation to encapsulate this
functionality in a business object, is not specific to any version of the framework. Reading those articles will help you to
understand how asynchronous operations actually work in the .NET.

GL
 

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