适当使用C#事件处理程序

时间:2012-04-06 20:32:46

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

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

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

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

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

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

任何时候只有一个组件应该能够请求更改系统状态,因此我需要使用互斥锁来防止并发状态更改。但是,我应该如何同步其余的这些组件对我来说是个谜。

我应该阅读任何建议或文献。我没有接受过C#语言的正式培训,所以如果我误报了什么,我会道歉。

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

5 个答案:

答案 0 :(得分:1)

  

我应该阅读任何建议或文献。我没有接受过C#语言的正式培训,所以如果我误报了什么,我会道歉。

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

请务必注意以下事项:

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

    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个(可以与连接检查合并)检查Internet连接。

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

答案 1 :(得分:1)

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

如果我是你,无论如何你重新开始,我会认真考虑 Rx Reactive Extensions

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

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

希望这有帮助,

答案 2 :(得分:0)

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

如果总有一个组件可以进行更改,并且您可以拥有多个组件,则使用某些同步机制(如您注明的Mutex)是完全有效的选择。

希望这有帮助。

答案 3 :(得分:0)

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

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

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

分离实际状态和目标状态允许用户随时改变主意,并使状态机转向所需的状态。

答案 4 :(得分:0)

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

关于禁用“连接”按钮的评论听起来对我说,虽然很可能你也可能需要其他形式的同步。但是,如果您的应用程序不需要多线程,那么我就不会为了它而引入多个线程。如果您这样做,请查看已作为Task Parallel Library的一部分包含的新Task API。他们很好地抽象了很多东西。

关于不过度思考这个问题的评论也很好。如果我在你的鞋子里,只是从一种新语言开始,我就会避免在开始时尝试使用这种架构。潜入,并使用您已经拥有的认知工具集开发它。当你探索更多时,你会发现,“哦,废话,这是一个更好的方法来做到这一点。”然后去那样做。重构是你的朋友。