Asynchronous Threading Issue

S

Sam Loveridge

Hi all. I'm relatively new to delegates and asynchronous threading and am
running into an issue. I need to asynchronously call a method (which I'm
doing with a delegate and BeginInvoke) and from the callback method, or at
some point after the EndInvoke has been called to end the asynchronous
operation I need to asynchronously call a different method.

I really want to keep this event based and use delegates and callbacks
rather than loop until completed, as there will be many instances of this
process running at the same time in parallel and I don't want to strain
resources.

The results are that the first method seems to be called multiple times.
Here's an example of the results of the test code I'll paste below ...

12:48:12 PM: 3932: Entering Main
12:48:12 PM: 3932: Calling Test1 asnchronously
12:48:12 PM: 2064: Entering Test1
12:48:12 PM: 2064: Exiting Test1
12:48:12 PM: 2064: Entering Test1Callback
12:48:12 PM: 2064: Calling EndInvoke in Test1Callback
12:48:12 PM: 2064: Calling Test2 asynchronously
12:48:12 PM: 2064: Exiting Test1Callback
12:48:12 PM: 2064: Entering Test1
12:48:12 PM: 2064: Exiting Test1
12:48:12 PM: 2064: Entering Test2Callback
12:48:12 PM: 2064: Calling EndInvoke in Test2Callback
12:48:12 PM: 3932: Exiting Main
12:48:14 PM: 2064: Entering Test2Callback
12:48:14 PM: 2064: Calling EndInvoke in Test2Callback

Notice that Test1 is called multiple times. Ideally I would like to see this
executed once. This is the behaviour I'm trying to rectify. Can anyone
suggest what I'm doing wrong? Here's the code for the test app (please be
sure to cut the sample code and results from any replies to this post to
keep the size down).

Thanks in advance, Sam.

VB CONSOLE APP CODE ...

Imports System
Imports System.Diagnostics

Module MainModule

Private Delegate Sub Test1Delegate()
Private Delegate Sub Test2Delegate()

Sub Main()
Console.WriteLine(String.Format("{0}: {1}: Entering Main",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.WriteLine(String.Format("{0}: {1}: Calling Test1 asnchronously",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))

Dim method As New Test1Delegate(AddressOf Test1)
Dim result As IAsyncResult = method.BeginInvoke(New
AsyncCallback(AddressOf Test1Callback), method)

Do Until result.IsCompleted
'
Loop

Console.WriteLine(String.Format("{0}: {1}: Exiting Main",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.ReadLine()
End Sub

Private Sub Test1()
Console.WriteLine(String.Format("{0}: {1}: Entering Test1",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.WriteLine(String.Format("{0}: {1}: Exiting Test1",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
End Sub

Private Sub Test1Callback(ByVal ar As IAsyncResult)
Console.WriteLine(String.Format("{0}: {1}: Entering Test1Callback",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.WriteLine(String.Format("{0}: {1}: Calling EndInvoke in
Test1Callback", DateTime.Now.ToLongTimeString(),
AppDomain.GetCurrentThreadId))

Dim method As Test1Delegate = DirectCast(ar.AsyncState, Test1Delegate)
method.EndInvoke(ar)

Console.WriteLine(String.Format("{0}: {1}: Calling Test2
asynchronously", DateTime.Now.ToLongTimeString(),
AppDomain.GetCurrentThreadId))

Dim method2 As New Test2Delegate(AddressOf Test2)
Dim result As IAsyncResult = method.BeginInvoke(New
AsyncCallback(AddressOf Test2Callback), method)

Console.WriteLine(String.Format("{0}: {1}: Exiting Test1Callback",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
End Sub

Private Sub Test2()
Console.WriteLine(String.Format("{0}: {1}: Entering Test2",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.WriteLine(String.Format("{0}: {1}: Exiting Test2",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
End Sub

Private Sub Test2Callback(ByVal ar As IAsyncResult)
Console.WriteLine(String.Format("{0}: {1}: Entering Test2Callback",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
Console.WriteLine(String.Format("{0}: {1}: Calling EndInvoke in
Test2Callback", DateTime.Now.ToLongTimeString(),
AppDomain.GetCurrentThreadId))

Dim method As Test2Delegate = DirectCast(ar.AsyncState, Test2Delegate)
method.EndInvoke(ar)

Console.WriteLine(String.Format("{0}: {1}: Exiting Test2Callback",
DateTime.Now.ToLongTimeString(), AppDomain.GetCurrentThreadId))
End Sub

End Module
 
K

Kevin Yu [MSFT]

Hi Sam,

First of all, I would like to confirm my understanding of your issue. From
your description, I understand that there is something wrong with the
multi-thread app, in which Test2 never get called. If there is any
misunderstanding, please feel free to let me know.

Based on the code you have provided, it seems there is a mistake in the
Test2CallBack. In your code, you called method.BeginInvoke to start the
thread. However, it has to be method2, because method's thread function has
been set to Test1. The following is the modified code. HTH.

Console.WriteLine(String.Format("{0}: {1}: Calling Test2
asynchronously", DateTime.Now.ToLongTimeString(),
AppDomain.GetCurrentThreadId))

Dim method2 As New Test2Delegate(AddressOf Test2)
Dim result As IAsyncResult = method2.BeginInvoke(New
AsyncCallback(AddressOf Test2Callback), method)

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."
 
S

Sam Loveridge

Silly me. Should have triple checked the sample code. I didn't call
method2.BeginInvoke and as a result it doesn't rally highlight the problem
I'm having at all.

The threading issue I'm trying to work through revolves around the
System.Net.Sockets.Socket.BeginConnect method. My understanding of async
callbacks is that if I use a callback function it will execute on a
different thread to the main thread. However, if I use a callback function
in the BeginConnect method and note the thread it would appear to be on the
main thread. This might not be the actual problem, but after that call
everything in my app goes haywire.

I want to call BeginConnect so that I try to connect without holding up
execution on the main thread, but if there's an error connecting I want to
retry automatically for x number of times. On success I want to then raise a
public event to notify success. I can get my code to perform the connection
attempt, and retries and after actually connecting it executes the public
"Connected" event once (traced to confirm), but when I trace the handler of
the event I see it has actually executed the number of times I tried to
connect.

Anyone else seen this? I use a similar form of code to the example I
attached earlier (which seems to perform correctly after fixing the bug :))
which leads me to suspect the way BeginConnect operates ???

Suggestions? Examples of using BeginConnect and retries with callbacks
instead of loops?

Sam.
 
J

Jon Skeet [C# MVP]

Sam Loveridge said:
Silly me. Should have triple checked the sample code. I didn't call
method2.BeginInvoke and as a result it doesn't rally highlight the problem
I'm having at all.

The threading issue I'm trying to work through revolves around the
System.Net.Sockets.Socket.BeginConnect method. My understanding of async
callbacks is that if I use a callback function it will execute on a
different thread to the main thread. However, if I use a callback function
in the BeginConnect method and note the thread it would appear to be on the
main thread. This might not be the actual problem, but after that call
everything in my app goes haywire.

How are you detecting whether or not you're on "the main thread"?
I want to call BeginConnect so that I try to connect without holding up
execution on the main thread, but if there's an error connecting I want to
retry automatically for x number of times. On success I want to then raise a
public event to notify success. I can get my code to perform the connection
attempt, and retries and after actually connecting it executes the public
"Connected" event once (traced to confirm), but when I trace the handler of
the event I see it has actually executed the number of times I tried to
connect.

Anyone else seen this? I use a similar form of code to the example I
attached earlier (which seems to perform correctly after fixing the bug :))
which leads me to suspect the way BeginConnect operates ???

Suggestions? Examples of using BeginConnect and retries with callbacks
instead of loops?

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that. (I know it'll be similar code to what you posted
before, but it's good to see the updated version.)
 
S

Sam Loveridge

Hi John.
How are you detecting whether or not you're on "the main thread"?

I use "AppDomain.GetCurrentThreadId() in a trace when in various parts of my
app as it executes to debug the problem. If a callback function is supposed
to operate on a worker thread, then I would expect to see a different thread
id when in my callback to the one that is displayed in the section of code
where I call BeginConnect, but they're the same. This may indicate that
maybe something under the hood of the Socket.BeginConnect code runs slightly
different to the way other delegates and callbacks are used, or I might just
be missing the point.
Could you post a short but complete program which demonstrates the
problem?

Yes, I'll try to put something together that's a cut down version of what
I've got as it involves all sorts of business process stuff that may cloud
the issue.

Sam.
 
J

Jon Skeet [C# MVP]

Sam Loveridge said:
I use "AppDomain.GetCurrentThreadId() in a trace when in various parts of my
app as it executes to debug the problem. If a callback function is supposed
to operate on a worker thread, then I would expect to see a different thread
id when in my callback to the one that is displayed in the section of code
where I call BeginConnect, but they're the same. This may indicate that
maybe something under the hood of the Socket.BeginConnect code runs slightly
different to the way other delegates and callbacks are used, or I might just
be missing the point.

If you were to call BeginConnect from a threadpool thread to start
with, you could well see the same thread id. That's the kind of thing I
was thinking about...
Yes, I'll try to put something together that's a cut down version of what
I've got as it involves all sorts of business process stuff that may cloud
the issue.

Great - thanks very much :)
 
S

Sam Loveridge

This is the point where I sneak away quietly with my tail between my legs.
As it turned out it wasn't a threading issue after all, but a place in my
code where I unwittingly added event handlers more than once, resulting in
my "Connected" event being called once after multiple connection attempts to
find it actually executed as many times as there were connection attempts. I
just moved the AddHandler statements higher up the food chain and it has
rectified the problem. In any case I've learnt a lot more about async
callbacks by digging in to the bowels of threading (a little icky, but
fascinating).

Thanks for your replies Kevin & Jon.

Regards,

Sam.
 
S

Sam Loveridge

This is the point where I sneak away quietly with my tail between my legs.
As it turned out it wasn't a threading issue after all, but a place in my
code where I unwittingly added event handlers more than once, resulting in
my "Connected" event being called once after multiple connection attempts to
find it actually executed as many times as there were connection attempts. I
just moved the AddHandler statements higher up the food chain and it has
rectified the problem. In any case I've learnt a lot more about async
callbacks by digging in to the bowels of threading (a little icky, but
fascinating).

Thanks for your replies Kevin & Jon.

Regards,

Sam.
 
K

Kevin Yu [MSFT]

You're welcome, Sam.

Thanks for sharing your experience with all the people here. If you have
any questions, please feel free to post them in the community.

Kevin Yu
=======
"This posting is provided "AS IS" with no warranties, and confers no
rights."
 
J

Jon Skeet [C# MVP]

Sam Loveridge said:
This is the point where I sneak away quietly with my tail between my legs.
As it turned out it wasn't a threading issue after all, but a place in my
code where I unwittingly added event handlers more than once, resulting in
my "Connected" event being called once after multiple connection attempts to
find it actually executed as many times as there were connection attempts. I
just moved the AddHandler statements higher up the food chain and it has
rectified the problem. In any case I've learnt a lot more about async
callbacks by digging in to the bowels of threading (a little icky, but
fascinating).

No problem - good to hear it's been solved :)
 

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