事件定义中为何出现 Lock关键字?

yuandonghuia 2012-03-21 08:47:46

namespace WrapTwoInterfaceEvents
{
using System;

public interface IDrawingObject
{
// Raise this event before drawing
// the object.
event EventHandler OnDraw;
}
public interface IShape
{
// Raise this event after drawing
// the shape.
event EventHandler OnDraw;
}


// Base class event publisher inherits two
// interfaces, each with an OnDraw event
public class Shape : IDrawingObject, IShape
{
// Create an event for each interface event
event EventHandler PreDrawEvent;
event EventHandler PostDrawEvent;

object objectLock = new Object();

// Explicit interface implementation required.
// Associate IDrawingObject's event with
// PreDrawEvent
event EventHandler IDrawingObject.OnDraw
{
add
{
lock (objectLock)
{
PreDrawEvent += value;
}
}
remove
{
lock (objectLock)
{
PreDrawEvent -= value;
}
}
}
// Explicit interface implementation required.
// Associate IShape's event with
// PostDrawEvent
event EventHandler IShape.OnDraw
{
add
{
lock (objectLock)
{
PostDrawEvent += value;
}
}
remove
{
lock (objectLock)
{
PostDrawEvent -= value;
}
}


}

// For the sake of simplicity this one method
// implements both interfaces.
public void Draw()
{
// Raise IDrawingObject's event before the object is drawn.
EventHandler handler = PreDrawEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
Console.WriteLine("Drawing a shape.");

// RaiseIShape's event after the object is drawn.
handler = PostDrawEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class Subscriber1
{
// References the shape object as an IDrawingObject
public Subscriber1(Shape shape)
{
IDrawingObject d = (IDrawingObject)shape;
d.OnDraw += new EventHandler(d_OnDraw);
}

void d_OnDraw(object sender, EventArgs e)
{
Console.WriteLine("Sub1 receives the IDrawingObject event.");
}
}
// References the shape object as an IShape
public class Subscriber2
{
public Subscriber2(Shape shape)
{
IShape d = (IShape)shape;
d.OnDraw += new EventHandler(d_OnDraw);
}

void d_OnDraw(object sender, EventArgs e)
{
Console.WriteLine("Sub2 receives the IShape event.");
}
}


public class Program
{
static void Main(string[] args)
{
Shape shape = new Shape();
Subscriber1 sub = new Subscriber1(shape);
Subscriber2 sub2 = new Subscriber2(shape);
shape.Draw();

// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}

}
/* Output:
Sub1 receives the IDrawingObject event.
Drawing a shape.
Sub2 receives the IShape event.
*/

上面一段是MSDN的源码,关于interface + event的,
让我不明白的是:下面,这里为何要用lock关键字, 还有定义了一个object。

object objectLock = new Object();

// Explicit interface implementation required.
// Associate IDrawingObject's event with
// PreDrawEvent
event EventHandler IDrawingObject.OnDraw
{
add
{
lock (objectLock)
{
PreDrawEvent += value;
}
}
remove
{
lock (objectLock)
{
PreDrawEvent -= value;
}
}
}


我自己模仿的时候如下:没用lock关键字和 那个object,也可以正常运行

interface myin
{
event mylele myevent;
}
class zi : fu,myin
{
//event mylele shijian
//{
// add { this.myevfu += value; }
// remove { this.myevfu -= value; }
//}
event mylele _mye;
event mylele myin.myevent
{
add
{
//ock (objectLock)
//{
_mye += value;
//}
}
remove
{

_mye -= value;

}
}
public void shuohua()
{
Console.WriteLine("shuohua:");
if(_mye!=null)
{
_mye(this);
}
}
}

我网上查资料的时候,发现lock关键字的相关信息都是在多线程的时候用的,这里涉及到多线程吗?为何这里要加这个,像我上面的代码那样不加,以后会不会造成什么潜在的漏洞,谢谢大神们~!!
...全文
221 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
yuandonghuia 2012-03-23
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 startstartsvip 的回复:]

引用 16 楼 yuandonghuia 的回复:

引用 15 楼 startstartsvip 的回复:

引用 12 楼 yuandonghuia 的回复:

引用 11 楼 wddw1986 的回复:

例子在哪里?能给个链接不?



就是你的这个

在 c# 4.0 以前 需要 lock 以后不需要


public class Shape : I……
[/Quote]
Event Object Locking

If you know what happens when you try to add an EventHandler or try to remove one event handler from an Event Accessor or even a public event you might know, the compiler places an explicit object lock when the event are added or removed. This is something like lock(this).

It is always better to avoid lock(this) as say you are using multiple threads accessing same object. If you invoke lock(this) it means the object will be locked to all other threads even though another thread tries to access another portion of the code. In case of C# less than 4.0, the compiler invokes explicit lock on the object when you want to add or remove a EventHandler from code. So it will look like:

private event EventHandler _myevent;
public event EventHandler MyEvent
{
add { lock (this) { this._myevent += value; } }
remove { lock (this) { this._myevent -= value; } }
}

Now if you see the event accessor for the event you will see _myevent accessor is almost similar to what i wrote for MyEvent accessor.


MethodImpl(MethodImplOptions.Synchronized)] attribute makes the object locked whenever the call to add event or remove event is made. This looks worse.

太牛了,结贴!!!
startstartsvip 2012-03-22
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 yuandonghuia 的回复:]

引用 11 楼 wddw1986 的回复:

这说起来就话长了,
先说 a+=1,这个操作在多线程中是不安全的,楼主,你理解了这个,就什么都好说了。
a+=1其实是两步操作 先把a的值取出来,然后+1,然后再写回a里面去
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了线程2.线程2把a的值取出来,+1,然后线程1,线程2分别……
[/Quote]

在 c# 4.0 中 那个例子中 不需要
zdzcool 2012-03-22
  • 打赏
  • 举报
回复
如果多个线程都用了同一个资源进行了注册,那这个事件的注册会是最后一个。
加了lock,就可以在别的线程注册时,注册不了,而保持已经注册了的事件的解决方法是对的。
[Quote=引用 8 楼 yuandonghuia 的回复:]

引用 6 楼 macooidle 的回复:

在多线程的时候可能有多个线程同时注册,要不lock可能会丢失的吧

同时注册为什么会丢失呢??
[/Quote]
startstartsvip 2012-03-22
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 yuandonghuia 的回复:]

引用 15 楼 startstartsvip 的回复:

引用 12 楼 yuandonghuia 的回复:

引用 11 楼 wddw1986 的回复:

例子在哪里?能给个链接不?

[/Quote]

就是你的这个

在 c# 4.0 以前 需要 lock 以后不需要


public class Shape : IDrawingObject, IShape
{
// Create an event for each interface event
event EventHandler PreDrawEvent;
event EventHandler PostDrawEvent;

event EventHandler IDrawingObject.OnDraw
{
add { PreDrawEvent += value; }
remove { PreDrawEvent -= value; }
}
}


参考

http://www.abhisheksur.com/2010/06/c-40-features.html
yuandonghuia 2012-03-22
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 wddw1986 的回复:]
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了线程2.线程2把a的值取出来,+1,然后线程1,线程2分别把值写回去。最后的结果就是a == 1,很明显这和你预期的 a == 2 是不相符的。
[/Quote]
原理是这样的啊?过程是先在内存中申请一个新地址,将a地址的值复制到新地址中,再在新地址中做加法,最后在把新地址中的值写入到a的地址中?对吧。值类型的“+=”都是这么操作?引用类型的“+=”是怎么个步骤呢?
ps:大神很牛,膜拜~!
yuandonghuia 2012-03-22
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 startstartsvip 的回复:]

引用 12 楼 yuandonghuia 的回复:

引用 11 楼 wddw1986 的回复:

这说起来就话长了,
先说 a+=1,这个操作在多线程中是不安全的,楼主,你理解了这个,就什么都好说了。
a+=1其实是两步操作 先把a的值取出来,然后+1,然后再写回a里面去
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了……
[/Quote]
例子在哪里?能给个链接不?
macooidle 2012-03-21
  • 打赏
  • 举报
回复
我估计编译成IL之后,你要是不Lock,几个事件会被注册到同一个内存地址,这就导致了冲突,原理应该和Singleton 模式差不多吧
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 macooidle 的回复:]

在多线程的时候可能有多个线程同时注册,要不lock可能会丢失的吧
[/Quote]
同时注册为什么会丢失呢??
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
我又重新看了看lock的定义,object那块明白了,但是这里为什么具体要这样用还是有点疑惑,可能是对整体的C#.net里面的流程不大明白吧。还有我想知道,如果我不加lock,当两个线程同时注册事件的时候,能发生什么冲突。。。。
macooidle 2012-03-21
  • 打赏
  • 举报
回复
在多线程的时候可能有多个线程同时注册,要不lock可能会丢失的吧
kkgoose 2012-03-21
  • 打赏
  • 举报
回复
关注下,object就是用于lock判断的,保证object同时只能被一个线程访问,感觉像flag标志位一样
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 bdmh 的回复:]
防止变量被其他线程同时修改,保证变量的稳定,你还是看看lock的帮助和线程的锁机制吧
[/Quote]
为什么这里要加他呢?为什么这里会涉及到多线程呢?还有为何他要定义一个毫不相关的object呢?大概的意思我也明白,我就想知道详细一些,我怕会给我搞的框架以后的稳定性带来隐患~
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
大神啊~~出现吧,这个是我查半天,不大明白才问的,我不是伸手直接要代码的小白。。。
bdmh 2012-03-21
  • 打赏
  • 举报
回复
防止变量被其他线程同时修改,保证变量的稳定,你还是看看lock的帮助和线程的锁机制吧
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
大神大神快出现~~~妈咪妈咪哄
gomoku 2012-03-21
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 wddw1986 的回复:]
...
本质不一样吗?
只要是值类型,如数组,这种先取值后修改,再写回去的操作都会有多线程的问题。
[/Quote]
赞一个。
cheng2005 2012-03-21
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 yuandonghuia 的回复:]

引用 11 楼 wddw1986 的回复:

这说起来就话长了,
先说 a+=1,这个操作在多线程中是不安全的,楼主,你理解了这个,就什么都好说了。
a+=1其实是两步操作 先把a的值取出来,然后+1,然后再写回a里面去
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了线程2.线程2把a的值取出来,+1,然后线程1,线程2分别……
[/Quote]
本质不一样吗?
只要是值类型,如数组,这种先取值后修改,再写回去的操作都会有多线程的问题。
yuandonghuia 2012-03-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 wddw1986 的回复:]

这说起来就话长了,
先说 a+=1,这个操作在多线程中是不安全的,楼主,你理解了这个,就什么都好说了。
a+=1其实是两步操作 先把a的值取出来,然后+1,然后再写回a里面去
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了线程2.线程2把a的值取出来,+1,然后线程1,线程2分别把值写回去。最后的结果就是a == 1,很明显这和……
[/Quote]
_mye += value;
是事件注册,和你说的+1什么的貌似不是一回事吧,注册的时候,真正追到委托那是有个链表的吧,同时注册不就是在这个链表上加值么,有没有什么像事物一样,必须一致的操作,干吗要lock呢?
cheng2005 2012-03-21
  • 打赏
  • 举报
回复
这说起来就话长了,
先说 a+=1,这个操作在多线程中是不安全的,楼主,你理解了这个,就什么都好说了。
a+=1其实是两步操作 先把a的值取出来,然后+1,然后再写回a里面去
假设初始a = 0;
线程1 a+=1.线程2 a+=1,
线程1,先把a的值取出来,+1,这时切换到了线程2.线程2把a的值取出来,+1,然后线程1,线程2分别把值写回去。最后的结果就是a == 1,很明显这和你预期的 a == 2 是不相符的。

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧