2012-02-28 52 views
2

我有这种功能。C中的闭包lambda表达式#

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); 
} 

的问题是,我得到IndexOutOfRangeExceptiondoneEvents[iter].Set();行代码时。请任何想法如何解决它?

+0

Eric Lippert写了一篇关于关闭循环变量的文章。看看http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – Alex 2012-02-28 09:31:18

回答

9

啊,好醇”在迭代变量关闭,P

移动int iter

int iter = i; 
ThreadPool.QueueUserWorkItem((o) => { 
    makeServiceCall(iter); 
}); 

目前正在捕获的东西,与循环变化,后通常会循环由线程踢入。或者,使用arg o

ThreadPool.QueueUserWorkItem((o) => { 
    int iter = (int)o; 
    makeServiceCall(iter); 
}, i); 

在物品排队时通过i的盒装副本,因此它不会更改。

如果这个问题不明确:被捕获到一个lambda变量保留其原有的申报点 - 这是一个完整的词法关闭。内QueueUserWorkItemi不是“在这个拉姆达创建时间的i价值”,而是为“的i现在值”。在大多数情况下,for循环将大幅度地缩短线程创建/拾取的时间,因此在线程启动时for循环已完成,并且i的值现在超出了该数组的范围。