C#中的闭包lambda表达式

本文关键字:lambda 表达式 闭包 | 更新日期: 2023-09-27 18:26:04

我有这样的函数。

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

问题是我在doneEvents[iter].Set();行代码时得到了IndexOutOfRangeException。求你了,有什么办法吗?

C#中的闭包lambda表达式

啊,迭代器变量上的好ol’闭包;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中的变量会保留其原始声明点——这是一个完整的词法闭包。QueueUserWorkItem中的i不是"创建此lambda时i的值",而是"现在i的值"。在大多数情况下,for循环将大大超出线程创建/拾取的速度,因此当线程开始时,for循环已经完成,i的值现在超出了数组的范围。