This is closely related to my last post on deferred execution gotchas and its basically more “if you inline delegated code, you may easily overlook scope side-effects”. This time it’s about dealing with foreach and using the local each item for deferred execution.
public void SpawnActions() { foreach (ActionContext context in contexts) { int id = context.Id; Action<int> callback = (workerNumber) => { Console.WriteLine("{0} Id: {1}/{2}", workerNumber, id, context.Id); }; ThreadPool.QueueUserWorkItem(new WaitCallback(FutureExecute), callback); } } public void FutureExecute(object state) { int id = worker++; Action<int> callback = state as Action<int>; Thread.Sleep(500); callback(id); }
The output looks like this:
0 Id: 0/9 1 Id: 1/9 2 Id: 2/9 3 Id: 3/9 4 Id: 4/9 5 Id: 5/9 6 Id: 6/9 7 Id: 7/9 8 Id: 8/9 9 Id: 9/9
So while the foreach scope variable context is kept alive for the deferred execution, it turns out that foreach re-uses the variable on each pass through the loop and therefore when the Action
foreach (ActionContext context in contexts) { int id = context.Id; // locally scoped variable ActionContext c2 = context; Action<int> callback = (workerNumber) => { Console.WriteLine("{0} Id: {1}/{2}", workerNumber, id, c2.Id); }; ThreadPool.QueueUserWorkItem(new WaitCallback(FutureExecute), callback); }
And now our results are a bit more what we expected:
0 Id: 0/0 1 Id: 1/1 2 Id: 2/2 3 Id: 3/3 4 Id: 4/4 5 Id: 5/5 6 Id: 6/6 7 Id: 7/7 8 Id: 8/8 9 Id: 9/9