gpt4 book ai didi

c# - 传输事件处理程序中的SynchronizationContext.Post(…)

转载 作者:行者123 更新时间:2023-12-04 00:31:09 25 4
gpt4 key购买 nike

我们有一种方法,由于客户端应用程序中的线程化,因此需要使用SynchronizationContext。

我的一位同事编写的一些代码对我来说并不“正确”,并且性能分析器告诉我,这段代码中使用了很多处理程序。

void transportHelper_SubscriptionMessageReceived(object sender, SubscriptionMessageEventArgs e)
{
if (SynchronizationContext.Current != synchronizationContext)
{
synchronizationContext.Post(delegate
{
transportHelper_SubscriptionMessageReceived(sender, e);
}, null);

return;
}
[code removed....]
}

这对我来说感觉不对,因为我们基本上是将相同的请求发布到gui线程事件队列中……但是,除了这部分代码的性能之外,我也看不到任何明显的问题。

此方法是一个事件处理程序,它附加到我们的中间层消息传递层帮助程序(transportHelper)引发的事件上,并且存在于处理来自GUI的请求的服务中。

这似乎是确保我们不会出现跨线程错误的可接受方法吗?如果没有,是否有更好的解决方案?

谢谢

最佳答案

让我们追溯一下该方法内部发生的情况,然后看看能告诉我们什么。

  • 方法签名遵循事件处理程序的签名,如问题所示,我们可以期望它在某个不是UI线程的线程的上下文中首先被调用。
  • 该方法要做的第一件事是将正在运行的线程的SynchronizationContext与保存在成员变量中的SynchronizationContext进行比较。我们假设保存的上下文是UI线程的上下文。 (Mike Peretz在CodeProject上向SynchronizationContext类发布了一系列精彩的入门文章)
  • 该方法将发现上下文不相等,因为它是在与UI线程不同的线程中调用的。调用线程的上下文很可能为空,其中确保将UI线程的上下文设置为WindowsFormsSynchronizationContext的实例。然后,它将在UI上下文上发出Post(),将委托(delegate)传递给自身及其参数,然后立即返回。这样就完成了对后台线程的所有处理。
  • Post()调用导致在UI线程上调用完全相同的方法。跟踪WindowsFormsSynchronizationContext.Post()的实现可以发现,这是通过在UI线程的消息队列上排队自定义Windows消息来实现的。在不复制或转换参数的意义上,参数已传递但未“编码(marshal)处理”。
  • 现在,由于Post()调用,使用完全相同的参数再次调用了我们的事件处理程序方法。但是,这次,线程的SynchronizationContext和保存的上下文是相同的。跳过if子句的内容,并执行[已删除代码]部分。

  • 这是一个好的设计吗?不知道[已删除代码]部分的内容就很难说。这里有一些想法:
  • 从表面上看,这似乎不是一个可怕的设计。在后台线程上收到一条消息,然后将其传递到UI线程进行演示。调用方立即返回以执行其他操作,接收方开始继续执行该任务。这有点类似于Unix fork()模式。
  • 该方法以独特的方式是递归的。它不会在同一线程上调用自身。而是,它导致一个不同的线程来调用它。与任何递归代码段一样,我们将关注其终止条件。从阅读代码来看,假定将其传递给UI线程时始终总是递归地调用一次,这似乎是相当安全的。但这是另一个要注意的问题。另一种设计可能已经将不同的方法传递给Post(),可能是匿名方法,并且完全避免了递归问题。
  • 似乎没有明显的理由在if子句中进行大量处理。回顾.NET reflector的Post()的WindowsFormsSynchronizationContext实现,发现其中包含一些适当长的代码序列,但是没有什么花哨的地方。这一切都发生在RAM中,并且不会复制大量数据。本质上,它只是准备参数并在接收线程的消息队列中排队Windows消息。
  • 您应该查看方法的[已删除代码]部分中发生的事情。涉及UI控件的代码完全属于此处-它必须在UI线程内执行。但是,如果其中有不处理UI的代码,则最好在接收线程中执行它。例如,任何占用大量CPU资源的解析都可以更好地托管在接收线程中,而不会影响UI响应能力。您可以将那部分代码移到if子句的上方,然后将其余代码移到一个单独的方法上-以确保这两个部分都不会执行两次。
  • 如果接收线程和UI线程都需要保持响应,例如为了进一步传入消息和用户输入,您可能需要引入第三个线程来处理消息,然后再将它们传递给UI线程。
  • 关于c# - 传输事件处理程序中的SynchronizationContext.Post(…),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1104108/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com