事件介绍
和委托类似,事件是后期绑定机制。 实际上,事件是建立在对委托的语言支持之上的。
事件是对象用于(向系统中的所有相关组件)广播已发生事情的一种方式。 任何其他组件都可以订阅事件,并在
事件引发时得到通知。
通过订阅事件,还可在两个对象(事件源和事件接收器)之间创建耦合。 需要确保当不再对事件感兴趣时,事件接
收器将从事件源取消订阅。
事件的访问器作用
涉及到线程安全的事件添加和移除操作。其作用是确保在多线程环境下对事件处理程序的添加和移除是安全的。具体来说:
-
线程安全:使用
Interlocked.CompareExchange
确保在多个线程同时操作事件处理程序时,不会导致数据竞争或更新不一致的问题。 -
添加处理程序:
add
访问器将新的处理程序添加到事件中,通过Delegate.Combine
合并现有的处理程序和新的处理程序。 -
移除处理程序:
remove
访问器从事件中移除指定的处理程序,通过Delegate.Remove
从现有处理程序列表中移除目标处理程序。
演示如何实现自定义的 add 和 remove 事件访问器。 虽然可以替换访问器内的任何代码,但建议先锁定事件,再添加或删除新的事件处理程序方法。
event EventHandler IDrawingObject.OnDraw
{
add
{
lock (objectLock)
{
PreDrawEvent += value;
}
}
remove
{
lock (objectLock)
{
PreDrawEvent -= value;
}
}
}
下面这个示范的事件访问器是我再写tcp通讯的时候用到的,因为代码比较多就列举了一个接收事件
private MyModbusSlave.ReceviceByteEventHandler _ReceviceByteEventHandler;
public delegate void ReceviceByteEventHandler(byte[] date);
//确保在多个线程同时进行事件处理程序的添加和移除时,操作是线程安全的。这种实现方式可以防止数据竞争和并发问题。
[Category("ModbusTcp事件")]
[Description("接收Byte数据事件")]
public event ReceviceByteEventHandler OnReceviceByte
{
add
{
//取得当前事件处理程序列表的副本。_ReceviceByteEventHandler 是存储事件处理程序的字段。
ReceviceByteEventHandler receviceByteEventHandler = this._ReceviceByteEventHandler;
//临时变量,用于在更新事件处理程序时进行比较。
ReceviceByteEventHandler temp;
//使用 do-while 循环来确保事件处理程序的更新是原子性的。如果在更新过程中,事件处理程序的列表被其他线程修改了,Interlocked.CompareExchange 将返回修改后的值,循环会重新尝试更新。
do
{
temp = receviceByteEventHandler;
//使用 Delegate.Combine 将新的处理程序 value 添加到当前事件处理程序列表中,并返回更新后的事件处理程序列表。
ReceviceByteEventHandler value2 = (ReceviceByteEventHandler)Delegate.Combine(receviceByteEventHandler);
//尝试将 _ReceviceByteEventHandler 更新为 value2。如果 _ReceviceByteEventHandler 的值等于 temp(表示在此期间没有其他线程修改事件处理程序列表),则更新成功。
receviceByteEventHandler = Interlocked.CompareExchange(ref _ReceviceByteEventHandler, value2, temp);
} while (receviceByteEventHandler != temp);
}
//取得当前事件处理程序列表的副本。
remove
{
ReceviceByteEventHandler receviceByteEventHandler = this._ReceviceByteEventHandler;
ReceviceByteEventHandler temp;
do
{
temp = receviceByteEventHandler;
//使用 Delegate.Remove 从当前事件处理程序列表中移除指定的处理程序 value,并返回更新后的事件处理程序列表。
ReceviceByteEventHandler value2 = (ReceviceByteEventHandler)Delegate.Remove(temp, value);
receviceByteEventHandler = Interlocked.CompareExchange(ref _ReceviceByteEventHandler, value2, temp);
} while (receviceByteEventHandler != temp);
}
}
通过 Interlocked.CompareExchange
和 Delegate.Combine
/ Delegate.Remove
实现了线程安全的事件处理程序添加和移除操作。这样可以避免在多线程环境下因并发修改事件处理程序列表而导致的问题,确保事件的处理程序在多个线程中正确地添加和移除。
什么情况下不使用事件访问器
在不使用这种线程安全的事件访问器的情况下,你可能会面临多线程环境下的竞态条件和数据一致性问题。
如果你的应用程序只在单线程环境下运行,或者你能够确保事件的添加和移除操作不会发生在多个线程之间的竞争,那么你可以不使用这种线程安全的实现。
但在大多数情况下,尤其是在复杂的多线程应用中,保持线程安全是很重要的,建议继续使用这种方式来避免潜在的问题。
想要更详细的了解和学习事件可以去这里: