【问题标题】:Appropriate usage of C# event handlers正确使用 C# 事件处理程序
【发布时间】:2012-04-20 09:25:15
【问题描述】:

我目前正在构建一个 C# 应用程序,当用户连接到特定的无线网络时,它将自动针对特定网络资源对用户进行身份验证。

目前,我正在使用Managed Wifi API 来发现用户何时连接/断开无线网络。我有一个事件处理程序,因此当任何这些活动发生时,都会调用我的一个方法来检查无线连接的当前状态。

为了管理应用程序的状态,我有另一个称为“导体”的类,它执行更改应用程序状态所需的操作。例如,当无线网卡连接到正确的网络时,指挥需要将系统状态从“正在监控”更改为“正在验证”。如果身份验证成功,指挥者需要将状态更改为“已连接”。断开连接再次导致“监控”状态,身份验证错误导致“错误”状态。这些状态更改(如果用户请求)可能会导致 TrayIcon 通知,因此用户知道他们正在通过身份验证。

我目前的想法是让用于检查无线当前状态的方法调用状态管理器中的“身份验证”或“断开连接”方法。但是,我不确定这是否是对事件处理程序的适当使用——它是否应该设置一个标志或通过某种形式的 IPC 将消息发送到一个单独的线程,该线程将开始身份验证/断开连接过程?

除了能够请求连接/断开连接的事件处理程序之外,用户还可以通过托盘图标执行它。因此,我需要确保这些后台操作不会阻止托盘与用户的交互。

应该只有一个组件可以随时请求更改系统状态,因此我需要使用互斥锁来防止并发状态更改。但是,我应该如何同步这些组件的其余部分对我来说有点神秘。

任何我应该阅读的建议或文献都会受到欢迎。我没有接受过正式的 C# 语言培训,所以如果我陈述错误,我深表歉意。

编辑:最重要的是,我想验证一个事件将作为一个单独的线程执行,因此它不会阻塞主 UI。另外,我想验证如果我有一个事件处理程序订阅了一个事件,它会串行处理事件,而不是并行处理(所以如果用户在处理第一个连接事件之前连接和断开连接,则不会发生两次状态更改同时发生)。

【问题讨论】:

标签: c# events event-handling thread-safety state-machine


【解决方案1】:

任何我应该阅读的建议或文献都会受到欢迎。我没有接受过正式的 C# 语言培训,所以如果我陈述错误,我深表歉意。

这解释了一些事情。 :)
我会阅读线程、事件处理和系统托盘图标/界面的创建。

请务必注意以下几点:

  • 事件在调用它们的同一线程上处理。如果您希望事件处理不锁定 GUI,那么您需要让按钮将工作移至不同的线程。
  • 当一个事件被触发时,它会将适当的参数传递给其列表中的所有方法。这与调用一个方法然后调用所有其他方法几乎相同(参见 EventFired 示例)。事件的目的不是调用我们已经可以做到的方法,而是调用编译代码时可能不知道的方法(当控件所在的库时,按钮控件上的单击事件将不知道)例如编译)。简而言之,如果您可以调用方法而不是使用事件,那就这样做吧。

    void EventFired(int arg1, object arg2)
    {
        subscribedMethod1(arg1, arg2);
        SubscribedMethod2(arg1, arg2);
        SubscribedMethod3(arg1, arg2);
        SubscribedMethod4(arg1, arg2);
        SubscribedMethod5(arg1, arg2);
        SubscribedMethod6(arg1, arg2);
        SubscribedMethod7(arg1, arg2);
    }
    
  • 如果您想防止用户界面锁定,请在另一个线程上进行操作。但请记住,用户界面元素(表单、按钮、网格、标签等)只能从它们的主机线程访问。使用 control.Invoke 方法调用其线程上的方法。

  • 从接口中删除一个选项并不是防止竞争状况的好方法(用户在一个已经运行的情况下开始连接/断开连接),因为用户界面将在不同的线程上并且可能不同步(它需要时间让单独的线程同步)。虽然有很多方法可以解决这个问题,但对于线程新手来说,最简单的方法是对值使用锁定。这样,.NET 将确保一次只有一个线程可以更改设置。您仍然需要更新用户界面,以便用户知道正在更新。

您的总体设计听起来不错。您可以使用 2-3 个线程(1 个用于用户界面(托盘图标),1 个用于检查新的网络连接,1 个(可以与连接检查合并)检查互联网连接。

希望这会有所帮助,如果您需要更多信息(或接受答案),请告诉我们。

【讨论】:

  • “如果您希望事件处理不锁定 GUI,那么您将需要让按钮将工作移动到不同的线程”。我在构建以前的应用程序时就认识到了这一点——单击按钮将启动另一个线程,该线程将通过 bool 停止(我知道您不想只是中止另一个线程)。但是,我正在讨论的事件(WlanNotificationEventHandler WlanNotification)是Managed Wifi Api 的一部分——订阅此事件的方法将在哪个线程上运行?控制我的 UI 的“主”线程?还是单独的线程?
  • @BSchlinker 我希望它将在您创建 WlanClient 实例的线程上运行。最简单的判断方法是在订阅事件的方法的第一行放置一个断点,运行程序并生成事件,这样断点就会被命中,打开线程面板(Debug\Windows \线程)。如果指针在绿色线程上,那么您正在主线程中运行。如果它在黄色线程上,那么您正在工作线程上运行。
  • 它在工作线程上运行,而不是在主线程上运行。那么,谁在启动工作线程?托管 Wifi API?
  • @BSchlinker 如果您从主线程创建 WlanNotificationEventHandler 实例,那么是的,托管 Wifi API 导致事件在工作线程上触发。实际上,它可能是创建一个工作线程来定期检查网络连接,当检测到更改时,工作线程开始执行订阅事件的方法。
【解决方案2】:

作为一种选择,另一种选择......

如果我是你,既然你要重新开始,我会认真考虑
Rx Reactive Extensions

它对事件和基于事件的编程提供了全新的视角,并且对您正在处理的事情有很大帮助(包括同步、处理线程、组合事件、停止、启动等)。

一开始学习可能有点“陡峭的曲线”,但同样,这可能是值得的。

希望这会有所帮助,

【讨论】:

    【解决方案3】:

    对我来说看来你要过度设计这个项目了。 您基本上需要在Commander 中实现一个事件并在主应用程序中订阅它们。那是。

    如果总是有一个组件可以进行更改并且您可以拥有多个组件,那么使用某种同步机制(例如您提到的Mutex)是完全有效的选择。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      如果您希望在任何时候最多有一个状态更改挂起,最好让您正在侦听的外部事件的事件处理程序在执行期间保持锁定。这确保了一种简单的编程方式,因为您可以保证您的应用程序的状态不会在您下面改变。在这种特殊情况下不需要单独的线程。

      您需要区分应用程序的当前状态和目标状态。用户指示目标状态(“已连接”、“已断开”)。实际状态可能不同。示例:用户想要断开连接,但实际状态是正在验证。身份验证步骤完成后,状态机必须检查目标状态:

      targetState == connected => set current state to connected
      targetState == disconnected => begin to disconnect and set state to disconnecting
      

      分离实际状态和目标状态允许用户随时改变主意,状态机可以引导到期望的状态。

      【讨论】:

        【解决方案5】:

        如果不了解应用的整个(建议的)结构,就很难给出准确的答案。但总的来说,是的,对这类事情使用事件处理程序是可以的 - 尽管我可能会将实际实现移到单独的方法中,以便您可以更轻松地从其他位置触发它。

        关于禁用“连接”按钮的评论对我来说听起来很正确,尽管可以想象您可能还需要其他形式的同步。但是,如果您的应用程序不需要多线程,我会避免仅仅为了它而引入多线程。如果您这样做,请查看已包含在 Task Parallel Library 中的新任务 API。他们很好地抽象了很多东西。

        关于不要过度思考这个问题的评论也很受欢迎。如果我站在你的立场上,只是从一门新语言开始,我会避免一开始就试图让架构恰到好处。深入研究并使用您已有的认知工具集进行开发。当你探索更多时,你会发现,“哦,废话,这是一个更好的方法。”然后去那样做。重构是你的朋友。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-22
          • 2016-05-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多