2011-08-12 112 views
1

我正在开发C#WinForms应用程序。它是一个MDI应用程序,其中包含两种形式。在任何时候,只有其中一种形式将被启用。该应用程序有两个线程。一个线程是主线程(驱动GUI的线程)。我还有另一个线程在后台运行,用于监听来自服务器的TCP消息(这是“客户”线程)。当我收到这些消息时,客户端线程将一个事件触发到GUI线程。因此,例如,这里有一个简单的例子中的代码如何我把一切都制定了:Winforms - 关于注册/取消注册事件的问题

public event EventHandler<ConnectEventArgs> ConnectEvent = delegate { }; 
    public event EventHandler<DisconnectEventArgs> DisconnectEvent = delegate { }; 
    public event EventHandler<EdgeMessageEventArgs> EdgeMessageEvent = delegate { }; 
    public event EventHandler<ServerModeEventArgs> ServerModeEvent = delegate { }; 
    public event EventHandler<SpreadDataEventArgs> SpreadDataEvent = delegate { }; 

形式#1注册事件:

 m_Client.ConnectEvent    += new EventHandler<ConnectEventArgs>(OnConnectEvent); 
     m_Client.DisconnectEvent   += new EventHandler<DisconnectEventArgs>(OnDisconnect); 
     m_Client.EdgeMessageEvent   += new EventHandler<EdgeMessageEventArgs>(OnEdgeMessageEvent); 

在客户端线程可能的事件列表

表#2登记为事件:

 m_Client.ConnectEvent += new EventHandler<ConnectEventArgs>(OnConnectEvent); 
     m_Client.DisconnectEvent += new EventHandler<DisconnectEventArgs>(OnDisconnect); 
     m_Client.SpreadDataEvent += new EventHandler<SpreadDataEventArgs>(OnSpreadDataEvent); 

主要形式(在MDI窗口)REG isters for one event:

 m_Client.ServerModeEvent += new EventHandler<ServerModeEventArgs>(OnServerModeEvent); 

当应用程序启动时,用户连接到服务器。只要客户端连接到服务器,就会触发ConnectEvent。在此之后,一个ServerModeEvent被触发。这基本上决定了应用程序中将使用哪种表单。我遇到的问题是如何以线程安全的方式完成这些表单中所有事件的注册/注销。

我最初的想法是在启动时禁用这两种表单,等待用户连接到服务器,然后仅启用基于服务器模式的其中一种表单。这种方法的问题在于注册特定表单上的事件的过程不会是线程安全的,因为当表单注册某些事件时,客户端线程可能触发事件。

我的下一个想法是提前注册两种形式的所有事件,然后连接到服务器,然后禁用其中一种形式。问题是类似的,被禁用的表单正在注销事件,而它可能会从客户端线程接收事件。

我听说过“软弱事件”,并认为这可能是我的问题的答案。我不知道如何在我的应用程序中实现这一点。有关我遇到的问题的任何想法?我可以使用弱事件吗?还有别的吗?

+1

如何为具有CurrentWindow属性的事件处理程序创建单独的类?你只需要切换该属性。而且你并不需要事件,一个界面会很好地完成。 –

回答

0

如果您使用Control.Invoke将事件从您的客户端线程发送到您的UI线程,客户端线程将被阻止,直到UI线程完成处理事件;这意味着当您在UI线程中设置事件处理程序时,您不可能引发异步事件。 (这是假设你的客户端线程实际上是一个单独的线程,它本身并不是异步的。)

但是,如果使用Control.BeginInvoke,UI线程处理事件时客户端线程不会阻塞。好处是客户端线程永远不必等待UI,但是折中的一点是,您可能需要事先在客户端对象上注册所有事件,因为它可能会在事件发生之前转向并触发事件UI线程有机会为它设置事件处理程序。事实上,在UI线程甚至有机会开始处理第一个事件(在这种情况下,它们排队并按UI线程顺序处理)之前,客户端线程可以发送多个事件。

然后,解决方案是在需要由客户端同步处理事件时使用Invoke;当你不这样做的时候开始启动。

+0

非常好的答案,但它与问题有什么关系? –

+0

这个问题是要求最好的方法来连接到来自后台线程的事件,同时考虑到后台线程可能会抛出事件的事实,*而你试图连接它,并且在你全部获得之前您的事件处理程序设置为处理这些事件。但是,如果从后台线程同步调用UI线程,则不存在该可能性,因为后台线程被阻止,并且直到UI线程完成设置所需的任何事件处理程序才能引发任何事件已经成立。 –

+0

蒂莫西弗里斯 - 谢谢你的答案。这非常有用。我会在某个时候尝试一下。所以,只是为了澄清......如果后台线程调用Control.Invoke,那么在UI注册事件的时候它根本没有机会触发另一个事件?换句话说,后台线程停留在Control.Invoke中,直到UI完成它在事件处理程序中需要做的任何事情? – Andrew