在C#中关闭lambda表达式

时间:2012-02-28 09:24:02

标签: c# lambda closures

我有这种功能。

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

问题是我在IndexOutOfRangeException代码行时收到doneEvents[iter].Set();。请问,任何想法如何解决?

1 个答案:

答案 0 :(得分: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中的变量保留其原始声明点 - 它是一个完整的词法闭包。 i里面的QueueUserWorkItem不是“创建此lambda时i的值”,而是“i 的值 /强>”。在大多数情况下,for循环将超出线程创建/拾取的大量余量,因此在线程启动时for循环已完成且值{{1}现在已超出数组范围。