c# foreach with Action.BeginInvoke



好吧,我这里有点问题。这是循环。

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

这是LazySync

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

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

如果你想获得更多信息,请在网上搜索:"访问修改过的闭包"。

您的怀疑是正确的:变量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帖子:

  • 访问修改后的闭包
  • C#重用foreach中的变量有什么原因吗
  • C#lambda,局部变量值在你想的时候没有取

尝试将c设置为局部变量,并在此基础上调用LazyAsync.Invoke,以避免c在调用发生之前被foreach循环重新分配。当LazyAsync.Invoke执行c.WriteMessage时,它调用的是c现在指向的任何WriteMessage,而不是评估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));
    }
}

最新更新