BeginInvoke and anonymous delegate

  • Thread starter Thread starter Dean Shimic
  • Start date Start date
D

Dean Shimic

void DisplayLines(object state)
{
for (int i = 0; i < 500; ++i)
{
int iCopy = i;
rtb.BeginInvoke((MethodInvoker)delegate
{
rtb.AppendText(iCopy + "\n");
});
}
MessageBox.Show("Done");
}

I call this function from the main thread with ThreadPool's
QueueUserWorkItem method. Since I'm accessing RichTextBox variable rtb from
the different thread than the one it was created on I have to call
BeginInvoke method.

Above code seems to work fine but I'm not sure if it's guaranteed to since
iCopy, the variable that is accessed from within the delegate, potentially
goes out of scope before delegate is invoked. Does this mean that iCopy can
be destroyed prior to delegate accessing it?
 
Hello, Dean!
You wrote on Sun, 3 Sep 2006 03:25:48 +0200:

DS> Above code seems to work fine but I'm not sure if it's guaranteed to
since
DS> iCopy, the variable that is accessed from within the delegate,
potentially
DS> goes out of scope before delegate is invoked. Does this mean that iCopy
can
DS> be destroyed prior to delegate accessing it?

iCopy will become a member of the hidden class
Have a look at for more details how anonymous delegates work
( http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx )
 
A better solution is to build the string on the background thread
(ideally using a StringBuilder instance), and then perform a *single*
BeginInvoke to the UI thread to "rtb.AppendText(sb.ToString());". This
is a: more efficient (less message posting etc), but more importantly,
b: it is simpler: you don't need to worry about the lifetime so much.
Oh, and c: it will cause less UI updates, making the UI thread more
efficient, and smoother... but the UI will update in a single jump. If
this is a problem, you could always to a .Invoke whenever for instance
i % 100 is zero (and then once at the end) - the .Invoke avoids any
question of accessing the captured variable from two threads, and you
still see a progressively growing UI...

Marc
 
Hello, Dean!
You wrote on Sun, 3 Sep 2006 03:25:48 +0200:

DS> Above code seems to work fine but I'm not sure if it's guaranteed to
since
DS> iCopy, the variable that is accessed from within the delegate,
potentially
DS> goes out of scope before delegate is invoked. Does this mean that iCopy
can
DS> be destroyed prior to delegate accessing it?

iCopy will become a member of the hidden class
Have a look at for more details how anonymous delegates work
( http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx )

Thanks.
 
A better solution is to build the string on the background thread
(ideally using a StringBuilder instance), and then perform a *single*
BeginInvoke to the UI thread to "rtb.AppendText(sb.ToString());". This
is a: more efficient (less message posting etc), but more importantly,
b: it is simpler: you don't need to worry about the lifetime so much.
Oh, and c: it will cause less UI updates, making the UI thread more
efficient, and smoother... but the UI will update in a single jump. If
this is a problem, you could always to a .Invoke whenever for instance
i % 100 is zero (and then once at the end) - the .Invoke avoids any
question of accessing the captured variable from two threads, and you
still see a progressively growing UI...

Marc

I know that building the string first would be more efficient but I
couldn't do that easily because the data (from the socket) comes in a
non-predictable way. I could get one message in 100 seconds or 100 messages
in 1 second.

As far as Invoke goes I believe it would give me some efficiency penalty
because the socket thread would have to wait for the string to be displayed
before it could continue.
 
You don't have to worry about this. The compiler generates a class
which is used to make the call to the anonymous delegate (which has the
method that is called). This class also will have members for the class
that contains this method (a field that ends in "this") as well as a member
for the iCopy variable.

In each iteration of the loop, a new instance of this class is created,
with the iCopy member field set, before the method to call is passed to
BeginInvoke.

Hope this helps.
 
Nicholas Paldino said:
You don't have to worry about this. The compiler generates a class
which is used to make the call to the anonymous delegate (which has the
method that is called). This class also will have members for the class
that contains this method (a field that ends in "this") as well as a member
for the iCopy variable.

In each iteration of the loop, a new instance of this class is created,
with the iCopy member field set, before the method to call is passed to
BeginInvoke.

Note that if you used "i" instead of "iCopy" you'd still get the same
behaviour - "i" would be captured in that case.
 
Good reasoning, but you didn't mention the sockets ;-p

One other approach (more suitable in the sockets case) is to batch the
updates in a container class, and simply have a timer on the UI thread that
sucks the new lines from the collection and clears it - dummied below (note
thread safety); this avoids lots of messaging in terms of .BeginInvoke /
..Invoke, and performs fewer UI updates (e.g. set the timer to 250ms or
something). In almost all cases the lock will be uncontested (so very fast -
probably faster than the .BeginInvoke step), and even when contested the
block is short-lived (we don't update the UI while holding the lock).

I've used this approach to great effect when dealing with mid-to-high volume
updates from multiple threads.

Marc

readonly StringBuilder newLines = new StringBuilder();
public void AddLinesTimer_Tick(object sender, EventArgs args) {
string toAdd;
lock(newLines) {
toAdd = newLines.ToString();
toAdd.Length = 0; // wipe
}
if(toAdd.Length > 0) {
someControl.Text += toAdd;
}
}

public void SomeWorker() {
for(some loop) {
string newLine = "blah";
if(newLine.Length > 0) {
lock(newLines) { // lock per iteration, not for the entire loop
newLines.AppendLine(newLine);
}
}
}
}
 
Note that if you used "i" instead of "iCopy" you'd still get the same
behaviour - "i" would be captured in that case.

Actually it doesn't. That is what I was using initially but if you add a
Thread.Sleep(10); in the delegate body output will end up being something
like 0, 500, 500, 500, etc.
 
Good reasoning, but you didn't mention the sockets ;-p

One other approach (more suitable in the sockets case) is to batch the
updates in a container class, and simply have a timer on the UI thread that
sucks the new lines from the collection and clears it - dummied below (note
thread safety); this avoids lots of messaging in terms of .BeginInvoke /
.Invoke, and performs fewer UI updates (e.g. set the timer to 250ms or
something). In almost all cases the lock will be uncontested (so very fast -
probably faster than the .BeginInvoke step), and even when contested the
block is short-lived (we don't update the UI while holding the lock).

I've used this approach to great effect when dealing with mid-to-high volume
updates from multiple threads.
[cut]

That's a clever way of doing it. I'll try and see how it behaves in my own
program.
 
Dean Shimic said:
Actually it doesn't. That is what I was using initially but if you add a
Thread.Sleep(10); in the delegate body output will end up being something
like 0, 500, 500, 500, etc.

Doh - you're absolutely right. The variable would be captured, not the
value. It's this kind of thing which suggests caution when it comes to
anonymous methods :) (Alternatively, it suggests not posting before the
first coffee of the morning...)
 

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

Back
Top