I have this kind of function.
function SomeFunction()
{
const int NUMBER_OF_CONCURENT_THREADS = 50;
List<Guid> sessions = new List<Guid>();
ManualResetEvent[] doneEvents = new ManualResetEvent[NUMBER_OF_CONCURENT_THREADS];
Action<int> makeServiceCall = (iter) =>
{
var proxy = GetProxy();
sessions.Add(proxy.GetCurrentSessionId());
doneEvents[iter].Set();
};
for (int i = 0; i < NUMBER_OF_CONCURENT_THREADS; ++i)
{
doneEvents[i] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((o) =>
{
int iter = i;
makeServiceCall(iter);
});
}
WaitHandle.WaitAll(doneEvents);
Assert.AreEqual(50, sessions.Count);
}
The problem is that I am getting IndexOutOfRangeException when at doneEvents[iter].Set(); line of code. Please, any ideas how to fix it?
Ah, good ol’ closure over the iterator variable ;p
Move the
int iter:Currently you are capturing something that is changing with the loop, and will usually be after the loop by the time the threads kick in. Alternatively, use the arg
o:This passes a boxed copy of
iat the time the item is queued, so it won’t change.In case the issue isn’t clear: variables that are captured into a lambda retain their original declaration point – it is a full lexical closure. The
iinsideQueueUserWorkItemis not “the value ofiat the time this lambda was created”, but rather, is “the value ofinow“. In most cases, theforloop will out-pace thread-creation/pickup by a massive margin, so by the time the threads have started theforloop has finished and the value ofiis now out of bounds for the array.