Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / June 2006

Tip: Looking for answers? Try searching our database.

lost in delegation

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Tim_Mac - 19 Jun 2006 11:35 GMT
hi,
i have encountered a strange behaviour when using ref parameters with
delegates.  basically the changed value is not reflected back to the
original calling method.

to prove the point, run this simple console application. it sets a
string variable 's' with the current number of Ticks, then invokes a
method via a delegate, waits for it to finish, and outputs the
supposed-to-be-changed value.  the value is changed in the method, but
is not reflected back.

using System;
using System.Threading;

namespace TestRef
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            new Class1();
        }

        public Class1()
        {
            string s = DateTime.Now.Ticks.ToString();
            Console.WriteLine("Initialised: \t\t" + s);

            // invoke a method using a delegate
            new TestByRefDelegate(this.TestByRef).BeginInvoke(ref s, null,
null);
            Thread.Sleep(500);  // wait until the other thread is finished
            Console.WriteLine("After delegate: \t" + s);

            Console.Read();
        }

        public delegate void TestByRefDelegate(ref string s);
        public void TestByRef(ref string s)
        {
            Console.WriteLine("Start of delegate: \t" + s);
            s = DateTime.Now.Ticks.ToString();
            Console.WriteLine("End of delegate: \t" + s);
        }
    }
}

can anyone shed some light on this?   i just realised that it's of
course because the value is changed on the new thread, but that is
disposed of as soon as it finishes.  this then begs the question, why
does the compiler allow delegates with ref parameters, since it is
impossible for the value to survive the lifetime of the thread?

thanks
tim
Barry Kelly - 19 Jun 2006 12:55 GMT
> i have encountered a strange behaviour when using ref parameters with
> delegates.  basically the changed value is not reflected back to the
[quoted text clipped - 5 lines]
> supposed-to-be-changed value.  the value is changed in the method, but
> is not reflected back.
[snip]

>             // invoke a method using a delegate
>             new TestByRefDelegate(this.TestByRef).BeginInvoke(ref s, null,
[quoted text clipped - 7 lines]
> does the compiler allow delegates with ref parameters, since it is
> impossible for the value to survive the lifetime of the thread?

Calling EndInvoke is not optional. It's never optional: you must always
call EndInvoke. It may acquire resources (such as synchronization
objects) which need to be released on the call to EndInvoke.

The value doesn't get returned until you call EndInvoke, because
otherwise the background thread would have a reference to a variable
allocated on the stack, and that's not allowed because the method might
return without ever calling EndInvoke(), which would allow the
threadpool thread executing the delegate to write to whatever random
stack frame that has replaced the calling frame.

Check this out (C# 2.0):

---8<---
using System;
using System.Threading;

class App
{
   delegate void TwiddleString(ref string value);
   
   static void Main()
   {
       // With End* call.
       TwiddleString twiddle = delegate(ref string value)
       {
           Thread.Sleep(500);
           value = "bar!";
       };
       
       string foo = "foo!";
       IAsyncResult res = twiddle.BeginInvoke(ref foo, null, null);
       
       Thread.Sleep(700);
       Console.WriteLine(foo);
       twiddle.EndInvoke(ref foo, res);
       Console.WriteLine(foo);
   }
}
--->8---

-- Barry

Signature

http://barrkel.blogspot.com/

Tim_Mac - 19 Jun 2006 16:19 GMT
hi Barry,
many thanks for your enlightening post.  i've been using Delegates for
a good long time never using EndInvoke, typically raising an event from
the delegate to signify the task completion. but this obviously isn't
the whole story!
thanks.
tim

> > i have encountered a strange behaviour when using ref parameters with
> > delegates.  basically the changed value is not reflected back to the
[quoted text clipped - 61 lines]
>
> -- Barry
Barry Kelly - 19 Jun 2006 18:16 GMT
> many thanks for your enlightening post.  i've been using Delegates for
> a good long time never using EndInvoke, typically raising an event from
> the delegate to signify the task completion. but this obviously isn't
> the whole story!

You're welcome. In C# 2.0, I've got a penchant for passing an anonymous
delegate argument to ThreadPool.QueueUserWorkItem() to get the same
effect (run something in the background), without needing to keep track
of an IAsyncResult. General pattern:

  ThreadPool.QueueUserWorkItem(delegate
  {
      // do stuff
  });

Of course, you've got to be careful of exceptions thrown. So, I actually
have library routines that run a delegate in the background, but wrap
the execution in an appropriate exception handler.

-- Barry

Signature

http://barrkel.blogspot.com/

Jon Skeet [C# MVP] - 19 Jun 2006 20:10 GMT
> > can anyone shed some light on this?   i just realised that it's of
> > course because the value is changed on the new thread, but that is
[quoted text clipped - 5 lines]
> call EndInvoke. It may acquire resources (such as synchronization
> objects) which need to be released on the call to EndInvoke.

Small exception to that - for Control.BeginInvoke, you don't need to
call EndInvoke. See
http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Barry Kelly - 19 Jun 2006 21:40 GMT
> > > can anyone shed some light on this?   i just realised that it's of
> > > course because the value is changed on the new thread, but that is
[quoted text clipped - 9 lines]
> call EndInvoke. See
> http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx

It's in the comment section:

CB> Last night I sent an email to the WinForms folks asking this same
CB> question. Like you, I suspect the EndInvoke is optional on Control.
CB> (I looked through the code, but that's no substitute for a statement
CB> from the authors). As you say, this API doesn't quite match the
CB> managed async programming model anyway. I'll reply back as soon as I
CB> get official word.

It's not exactly definitive! I like to stick with always matching with
the End* method, to be safe. Normally, I rather use
ThreadPool.QueueUserWorkItem() where I can (like I mentioned in the
other message). For async methods where they're needed (such as server
IO to avoid lots of redundant blocked threads), well, I try to avoid
that until it's demonstrably necessary.

-- Barry

Signature

http://barrkel.blogspot.com/

Jon Skeet [C# MVP] - 22 Jun 2006 23:37 GMT
> > Small exception to that - for Control.BeginInvoke, you don't need to
> > call EndInvoke. See
[quoted text clipped - 8 lines]
> CB> managed async programming model anyway. I'll reply back as soon as I
> CB> get official word.

You missed the bit later on - Chris Brumme again:

<quote>
Chris Brumme said:
I just got the official word from the WinForms team. It is not
necessary to call Control.EndInvoke. You can call BeginInvoke in a
"fire and forget" manner with impunity.
May 12, 2003 5:30 PM
</quote>

I know it's on a blog rather than being part of the documentation, but
I think it's still reasonably definitive. Let's say I trust Chris
Brumme when it comes to this kind of thing :)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jon Shemitz - 23 Jun 2006 00:20 GMT
> I know it's on a blog rather than being part of the documentation, but
> I think it's still reasonably definitive. Let's say I trust Chris
> Brumme when it comes to this kind of thing :)

I do, too. The blog certainly influenced my explanation of
Control.Invoke behavior.

Signature

Thank you for reading this fine message from the author of

.NET 2.0 for Delphi Programmers        www.midnightbeach.com/.net
Delphi skills make .NET easy to learn  In print, in stores.

Barry Kelly - 23 Jun 2006 00:40 GMT
> You missed the bit later on - Chris Brumme again:
>
[quoted text clipped - 5 lines]
> May 12, 2003 5:30 PM
> </quote>

I did miss that, yes. This prompted me to open up Reflector and peek a
little further - and I was disappointed by what I found.

The upshot is that it's possible that a ManualResetEvent is leaked and
not disposed every time you (i) call BeginInvoke() and (ii) access the
AsyncWaitHandle on the returned IAsyncResult.

> I know it's on a blog rather than being part of the documentation, but
> I think it's still reasonably definitive. Let's say I trust Chris
> Brumme when it comes to this kind of thing :)

I have less trust in the WinForms team themselves. They have
mis-implemented the Dispose/Finalize pattern on the
Control.ThreadMethodEntry class. An instance of this class is returned
when you call BeginInvoke().

The situation: a ManualResetEvent is allocated if you ever get the value
of the AsyncWaitHandle property (this is lazy allocation, and is to be
expected and is good). This ManualResetEvent internally holds a
SafeHandle which wraps the real OS-level object, and is, of course, a
critical finalizable object. IMO, that doesn't excuse the developer from
disposing of it whenever they reasonably can. GC is for memory not
resources etc etc.

The problems:

1) The Control.ThreadMethodEntry class has no method which disposes of
this contained ManualResetEvent, so even if one calls
Control.EndInvoke(), a critical resource will be leaked and the GC
finalizer thread is relied upon to collect it.

2) The Control.ThreadMethodEntry class implements a pointless finalizer
which calls Close on the internal ManualResetEvent if it exists. This
indicates a misunderstanding of GC and finalization: finalization
routines shouldn't access any references they contain to other managed
classes, because they may have been collected and finalized themselves.
This is the whole purpose behind the boolean argument to the virtual
protected Dispose(bool) method in the Dispose pattern.

In effect, the folks in the WinForms team implemented a Finalizer on
Control.ThreadMethodEntry instead of IDispose. They should have
implemented IDispose and called Dispose() from EndInvoke(), and stayed
well away from Finalize(), which is very rarely needed.

-- Barry

Signature

http://barrkel.blogspot.com/

Peter Bromley - 26 Jun 2006 02:47 GMT
>>>Small exception to that - for Control.BeginInvoke, you don't need to
>>>call EndInvoke. See
[quoted text clipped - 22 lines]
> I think it's still reasonably definitive. Let's say I trust Chris
> Brumme when it comes to this kind of thing :)

One nit.

If an exception is thrown within your delegate, you will only get the
exception throw to you if you call EndInvoke

--

Peter Bromley

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.