Using Delegates; An "Oh for F**** sake" moment

Monday, May 11, 2009 / Posted by Luke Puplett /

"Stay away from the nice doggy, children."

While reading the excellent (if somewhat late) Concurrent Programming on Windows: Architecture, Principles, and Patterns (Microsoft .Net Development) I began trying to recall the countless times that I have taken advantage of the framework's APM methods you get for free when you new up a delegate. Here's the noteworthy passage from page 418:

All delegate types, by convention, offer a BeginInvoke and EndInvoke method alongside the ordinary synchronous method. While this is a nice programming model feature, you should stay away from them wherever possible. The implementation uses remoting infrastructure that imposes a sizeable overhead...

Joe's choice of the word nice being a colloquial English word meaning not nice, shit, a poor effort. The scarcity of information from reliable sources and the arguments and proliferation of inaccuracies in the area of concurrency in .NET had me guessing that Joe's book would contain some nasty surprises which is why I'm somewhat peaved that such an important reference has only just come available, eight years after the Framework was released - and two years into my own person journey of asynchronous discovery! --grrrr.

Labels: , , ,

3 comments:

Comment by Unknown on Friday, June 26, 2009

You know, I was writing up some interview questions about this exact topic and ran across this quote from Joe. I'm wondering if it is a typo. It seems to me the implementation may use reflection rather than remoting. I can not understand why remoting would be used since that is for communication...any thoughts?

gschmitt99@gmail.com

Comment by Luke Puplett on Saturday, June 27, 2009

Hi g, firstly I apologise for taking a while to publish your comment. Secondly, I hope I've not inadvertantly published your email address which is visible to me in the post body, if you didn't wish to have it public. I can't seem to edit a comment to remove it.

Regarding what you said, its something that I did wonder myself and my first thought was to reach for Reflector, followed by the thought that these methods are added by the compiler. I will endeavor to do two things: email Joe and ask, and I'll see if its possible to compile and inspect the BeginInvoke methods. An interesting side is that Remoting and Reflection won't work in a Partially Trusted context and I wonder what happens in such circumstances.

I notice from my stats that the single biggest geographic readership of this page is Redmond, and so if someone on the 'inside' can help, then please step forward.

Furthermore, whatever the outcome, I think code using this convenience would need testing to see if the workloads are too small and the benefits of concurrency are outwayed by the overhead. --I can't see a problem with kicking off a large task like this, but guidance says that the threadpool is for 'short' bursts.

Comment by Luke Puplett on Wednesday, July 08, 2009

The answer, or rather the technique to inspecting how it works on the inside is actually all to obvious once someone smarter than me has pointed it out.

As Joe was busy, I wrote to Stephen Toub and got not just confirmation but proof:

___ Stephen's reply ___

Hi Luke-



Joe is correct when he states that asynchronous delegate invocation uses remoting. As an example and a hint to this fact, run the following code and take a look at the call stacks output:



using System;

using System.Diagnostics;



class Program

{

static void Main(string[] args)

{

Action a = Foo;

a.BeginInvoke(ar =>

{

a.EndInvoke(ar);

Console.WriteLine("-----------------------------");

Console.WriteLine(new StackTrace().ToString());

}, null);



Console.ReadLine();

}



static void Foo()

{

Console.WriteLine("-----------------------------");

Console.WriteLine(new StackTrace().ToString());

}

}



For reference, here’s the output I see:



-----------------------------

at Program.Foo()

at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(

IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInCont

ext, Object[]& outArgs)

at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMe

ssage msg, IMessageSink replySink)

at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.DoAsyncCall()

at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Ob

ject o)

at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object sta

te)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C

ontextCallback callback, Object state, Boolean ignoreSyncCtx)

at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWor

kItem.ExecuteWorkItem()

at System.Threading.ThreadPoolWorkQueue.Dispatch()

at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()



-----------------------------

at Program.<>c__DisplayClass1.Main>b__0(IAsyncResult ar)

at System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage

msg)

at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMe

ssage msg, IMessageSink replySink)

at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.DoAsyncCall()

at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Ob

ject o)

at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object sta

te)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C

ontextCallback callback, Object state, Boolean ignoreSyncCtx)

at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWor

kItem.ExecuteWorkItem()

at System.Threading.ThreadPoolWorkQueue.Dispatch()

at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()



Hope that helps,

Stephen

Post a Comment