c#foreach with Action.BeginInvoke

时间:2012-01-25 20:04:02

标签: c# concurrency delegates action invoke

好吧,所以我在这里遇到了一些问题。 这是循环。

lock (ClientLocker)
{
    Trace.WriteLine("#WriteAll: " + sm.Header);
    foreach (Client c in Clients)
    {
        if (c.LoggedIn)
        {
            Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
            LazyAsync.Invoke(() => c.WriteMessage(sm));
        }
    }
}

这是LazyAsync

public static class LazyAsync
{
    public static void Invoke(Action a)
    {
        a.BeginInvoke(a.EndInvoke, null);
    }
}

每个Client都包含socket,因此我不能Clone。 问题是,当我执行Invokec.WriteMessage时,由于执行被延迟,它通常不会触发列表中的第一对,有时实际上只会触发一大堆最后一项。

我知道这与c是一个在Invoke实际被调用之前发生变化的引用有关,但有没有办法避免这种情况?

执行常规for(int i=0 etc循环似乎无法解决此问题。

任何人对我如何解决这个问题都有任何想法?

请注意,不能Clone Client

3 个答案:

答案 0 :(得分:5)

c复制到本地变量,如下所示:

lock (ClientLocker)
{
    Trace.WriteLine("#WriteAll: " + sm.Header);
    foreach (Client c in Clients)
    {
        if (c.LoggedIn)
        {
            Client localC = c;
            Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
            LazyAsync.Invoke(() => localC.WriteMessage(sm));
        }
    }
}

如果您想获得更多信息,请进行网络搜索:“访问已修改的关闭”。

答案 1 :(得分:1)

您的怀疑是正确的:变量c由lambda表达式捕获,但直到稍后才进行评估。

每当你在lambda表达式中使用循环变量时,都会弹出这种错误,因为循环变量的作用域在循环之外,而不是循环的每次迭代。

您可以通过在foreach循环中创建一个新的局部变量,为其分配c,然后将该新的局部变量传递给lambda表达式来解决此问题:

lock (ClientLocker)
{
    Trace.WriteLine("#WriteAll: " + sm.Header);
    foreach (Client c in Clients)
    {
        if (c.LoggedIn)
        {
            Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");

            Client copyOfC = c;
            LazyAsync.Invoke(() => copyOfC.WriteMessage(sm));
        }
    }
}

以下是一些相关的StackOverflow帖子:

答案 2 :(得分:1)

尝试将c设置为本地变量并在其上调用LazyAsync.Invoke,以避免在调用发生之前c循环重新分配foreach。当LazyAsync.Invoke执行c.WriteMessage时,它会在WriteMessage现在指向的任何内容上调用c,而不是在评估LazyAsync.Invoke(() => c.WriteMessage(sm))时的内容

foreach (Client c in Clients)
{
    if (c.LoggedIn)
    {
        Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");

        Client client = c;
        LazyAsync.Invoke(() => client.WriteMessage(sm));
    }
}