Alright, so I'm having a bit of an issue here. Here is the loop.
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
LazyAsync.Invoke(() => c.WriteMessage(sm));
}
}
}
Here is LazyAsync
public static class LazyAsync
{
public static void Invoke(Action a)
{
a.BeginInvoke(a.EndInvoke, null);
}
}
Each Client
contains a socket
, so I can't hardly Clone
it. The problem is, when I do the Invoke
to c.WriteMessage
, since the execution is delayed, it usually won't fire on the first couple in the list, and will sometimes actually only fire a whole bunch on the very last item.
I know this has to do with c being a reference that changes before Invoke
actually gets called, but is there a way to avoid this?
Doing a general for(int i=0 etc
loop doesn't seem to fix this issue.
Anyone have any ideas on how I can fix this?
Remember, can't Clone
Client
.
Copy your c
to local variable like this:
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Client localC = c;
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
LazyAsync.Invoke(() => localC.WriteMessage(sm));
}
}
}
Do a web search for: "Access to modified closure" if you want to get more information.
Your suspicion is right: the variable c
is captured by the lambda expression, but not evaluated until later.
This flavor of error pops up whenever you make use of a loop variable within a lambda expression, since the loop variable is scoped outside the loop, and not with each iteration of the loop.
You can work around this by creating a new local variable in the foreach
loop, assign c
to it, and then pass that new local variable into the lambda expression:
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
Client copyOfC = c;
LazyAsync.Invoke(() => copyOfC.WriteMessage(sm));
}
}
}
Here are a few related StackOverflow posts:
Try setting c
to a local variable and calling LazyAsync.Invoke
on that, to avoid c
being reassigned to by the foreach
loop before the invoke happens. When LazyAsync.Invoke
does c.WriteMessage
, it's calling WriteMessage
on whatever c
happens to now point to, not what it was when LazyAsync.Invoke(() => c.WriteMessage(sm))
was evaluated
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
Client client = c;
LazyAsync.Invoke(() => client.WriteMessage(sm));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.