views:

93

answers:

3

I've the following code in my app.

MyEventHandler handler = null; //Declare the handler

foreach (string pname in group)
{
  handler = getHandler(pname); //Get the handler
  if(handler == null)
  {                        
      throw new KeyNotFoundException("No user " + pname + " could be found");
  }
  //invoke the handler
  handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
}

So i get the handler and call BeginInvoke method. But before BeginInvoke gets called it goes to next iteration and the handler value gets changed. So the BeginInvoke is getting involved for this new handler.

Hope you get my point. So how can i eliminate this issue? I dont want to call sleep after BeginInvoke as i feel it is a loss of time.

Any ideas?

Update1 I'm pretty sure that the handler object gets changed before BeginInvoke() is called. I guess that the BeginInvoke takes some time to create a separate thread to call the other function.

Update2 This code is in a WCF service and the clients call a function which in turn makes use of this function. I've separate handlers stored in my server for each client. The WCF service has a duplex contract with separates sessions for the client. I see that after this function is executed same user is getting invoked twice. But i put a break point and debug it (which gives the BeginInvoke the necessary time to call the function) it works "PERFECTLY". I very sure i faced this problem in threading too where i create multiple threads in a loop. If the thread delegate has parameters a,b,c and if you change it at the beginning of the next iteration the same behavior occurs. I dono how many of you people have experienced this issue before. If i put a Sleep() or if i make a copy of the handler and invoke it using copy it'll work.

Update3

Okie, i've tested it now. I just added the Thread.Sleep() as follows.

chatTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
Thread.Sleep(500);

and it is working like a charm. Any thoughts?

Update 4

I've created a thread sample demonstrating the issue and i've uploaded it here. I hope a solution to this will resolve my issue too. Kindly check the sample.

A: 

I think you're having some other problem... handler.BeginInvoke can't be called after next iteration, you're still in the same thread...

astorcas
I'm not using that handler object anywhere else. When you call the BeginInvoke will the control of the application waits till the BeginInvoke invokes the delegate?
NLV
i've updated my post.
NLV
No, it will not, but BeginInvoke has taken a copy of the current value of handler, and will not refer to the variable it came from, only to its copy, which means that even if you change handler right after returning from BeginInvoke, **it shouldn't matter**
Lasse V. Karlsen
Hello Karlsen. I've updated the post. Download the thread sample from here - http://cid-3ef70212ea082a2d.office.live.com/self.aspx/.Public/ThreadIssueSample.zip
NLV
+1  A: 
Andras Zoltan
Regardless of whether BeginInvoke happens at once, it takes a snapshot during the initial startup call (the one being made from the shown code), so even if handler changes in the loop before BeginInvoke has started executing the code in the background, it shouldn't matter.
Lasse V. Karlsen
Hmmm. May be i'm wrong but i really doubt what you're saying. Why does the application work fine when i put a sleep or do it with a copy object?
NLV
i've updated my post.
NLV
It is impossible to say why it works as you expect when you add a sleep because **we're not seeing the whole picture**. All I can tell you is that when you do `handler.BeginInvoke`, changes to handler after BeginInvoke has returned, but before it starts executing the code in the background **does not matter**. Which handler to execute in the background was copied when BeginInvoke was executed, `handler` is not used by BeginInvoke so changes to that variable **does not matter**.
Lasse V. Karlsen
Thanks for your comment Karlsen. Let me dig in more.
NLV
@Lasse V. Karlsen: re your first comment - that's exactly what I said at the end of the first paragraph. From the original code that was posted, there's no way that local handler variable (unless it's a class member, or perhaps a ref argument passed to the method and the caller is on a separate thread) can be getting modified by anything other than the loop itself, in which case, I question whether the behaviour the OP describes is actually happening at all.
Andras Zoltan
Yep, I think I posted my comment in the wrong comment box here :) I was not commenting your answer.
Lasse V. Karlsen
I'm marking this as answer because this is the real answer as i've understood - "the handler to be invoked is captured as soon as BeginInvoke is called, so it won't matter if the local variable changes afterwards."
NLV
+3  A: 

Ok, after your fourth edit you provide us with an example, that exhibits a problem, sure, but not the problem you're asking for help about.

What you're saying in the question is that:

  • I use BeginInvoke on a delegate variable, then change the variable, and somehow my delegates are invoked twice

What you exhibit in the posted code is that:

  • I capture a loop variable in an anonymous method, and somehow I use the wrong variable value

THESE ARE NOT THE SAME PROBLEM!

The reason for your posted code misbehaving is that the code in question actually looks like this under the hood:

int i;
for (i = 0; i < 10; i++)
    ... create delegate, capture i, spawn thread

Here you're capturing the same variable for all the threads. If a thread doesn't start executing before the loop changes the variable, then yes, you will see the "incorrect value" for that variable.

However, if you change the code like this:

for (int i = 0; i < 10; i++)
{
    int j = i;
    ThreadStart threadStartObj = new ThreadStart(
        delegate { PrintValueThreadFunction(j, j); });
                                            ^
                                            |
                                            +-- use j instead of i here

Then you will capture a "fresh" variable for each thread, which won't get changed.

So, the question remains. Is this the problem you're having? If so, then shame on you, next time, don't simplify the problem. You're wasting people's time, most of all your own. Had you posted code like the above to begin with you would've had an answer (or a duplicate question pointing to existing answers, there's plenty) within a couple of minutes.

If this is not the problem you're having, you're still having problem with an event handler like in the original code being invoked more than once, go back and produce a better example project.

Lasse V. Karlsen
+1 for attempting to help far beyond the call of duty...
Dan Puzey
The sad thing here is that when I looked at the code in notepad I immediately knew what the problem was, as would about 100+ other people here on SO within the first minute, without actually compiling or executing the code at all.
Lasse V. Karlsen
Firstly, i know i dint give the exact code of my issue. Thats why i've quoted "I hope a solution to this will resolve my issue too. Kindly check the sample." I just guessed whether i have the same problem in my code since BeginInvoke creates a new thread. Thats what i've quoted in #Update1.
NLV
Also in Update2 i have quoted the same thing you have said here, which is to make a copy of the variable. And, i've not simplified my code much. I've just posted a part of the code. May be it is not enough, i understand. But i've told that handler is a local variable and i dont change it anywhere else. I'm not that bad :).
NLV
+1 Lasse, excellent answer.
Andras Zoltan
But making a copy of handler wouldn't make a difference, because in your code you're not using anonymous methods or capturing variables. It is not the same issue. Whether you're making copy of handler or not, in the code shown in your question, has no bearing on the problem at all. The problem is entirely different, and has to do with anonymous delegates and captured variables, not with BeginInvoke. In short, don't simplify for our benefit until we ask for it.
Lasse V. Karlsen