时间:2019-03-17 标签:c#foreachwithAction.BeginInvoke

发布于 2024-12-28 20:38:06 字数 911 浏览 1 评论 0原文

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

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,所以我几乎无法克隆它。 问题是,当我对 c.WriteMessage 执行 Invoke 时,由于执行被延迟,它通常不会在列表中的第一对上触发,并且会有时实际上只在最后一个项目上发射一大堆。

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

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

有人知道如何解决此问题吗?

记住,不能克隆 客户端

Alright, so I'm having a bit of an issue here.
Here is the loop.

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

Here is LazyAsync

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

Each Client contains a socket, so I can't hardly Clone it.
The problem is, when I do the Invoke to c.WriteMessage, since the execution is delayed, it usually won't fire on the first couple in the list, and will sometimes actually only fire a whole bunch on the very last item.

I know this has to do with c being a reference that changes before Invoke actually gets called, but is there a way to avoid this?

Doing a general for(int i=0 etc loop doesn't seem to fix this issue.

Anyone have any ideas on how I can fix this?

Remember, can't Clone Client.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫瑾少女 2025-01-04 20:38:06

将您的 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));
        }
    }
}

如果您想获取更多信息,请在网络上搜索:“访问修改后的闭包”。

Copy your c to local variable like this:

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

Do a web search for: "Access to modified closure" if you want to get more information.

深空失忆 2025-01-04 20:38:06

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

Your suspicion is right: the variable c is captured by the lambda expression, but not evaluated until later.

This flavor of error pops up whenever you make use of a loop variable within a lambda expression, since the loop variable is scoped outside the loop, and not with each iteration of the loop.

You can work around this by creating a new local variable in the foreach loop, assign c to it, and then pass that new local variable into the lambda expression:

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

Here are a few related StackOverflow posts:

最舍不得你 2025-01-04 20:38:06

尝试将 c 设置为局部变量并对其调用 LazyAsync.Invoke,以避免 cforeach 在调用发生之前循环。当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));
    }
}

Try setting c to a local variable and calling LazyAsync.Invoke on that, to avoid c being reassigned to by the foreach loop before the invoke happens. When LazyAsync.Invoke does c.WriteMessage, it's calling WriteMessage on whatever c happens to now point to, not what it was when LazyAsync.Invoke(() => c.WriteMessage(sm)) was evaluated

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

        Client client = c;
        LazyAsync.Invoke(() => client.WriteMessage(sm));
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文