文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>以线程安全方式引发事件

以线程安全方式引发事件

时间:2010-10-13  来源:neutra

  《CLR via C#》3rd中提到,应该以线程安全的方式引发事件,不禁冒冷汗,一直以来还真没注意到这个问题,以前写的不少代码得重新审查修正了。下面是引用原文说明:

.Net Framework最初发布时,是建议开发者用以下方式引发事件:

protected virtual void OnNewMail(NewMailEventArgs e)
{
if (NewMail != null) NewMail(this, e);
}

这个OnNewMail方法的问题在于,线程可能发现NewMail不为null,然后,就在调用NewMail之前,另一个线程从委托链中移除了一个委托,是NewMail变成了null。这会造成抛出一个NullReferenceException异常。

  于是我写了以下代码测试重现这种线程竞态的情况:

  

代码
using System;
using System.Threading;
using System.Diagnostics;

namespace Neutra.Utils
{
class EventTest
{
public event EventHandler MyEvent;

static void Main(string[] args)
{
Console.WriteLine(
"Test1 start");
Test1();
Console.WriteLine(
"Test1 end");

Console.WriteLine(
"Test2 start");
Test2();
Console.WriteLine(
"Test2 end");
}

static void AddAndRemoveEventHandler(object obj)
{
var instance
= obj as EventTest;
for (int i = 0; i < 100000; i++)
{
instance.MyEvent
+= HandleEvent;
Thread.Sleep(
0);
instance.MyEvent
-= HandleEvent;
Thread.Sleep(
0);
}
}

static void HandleEvent(object sender, EventArgs e)
{
Console.Write(
'>');
}

static void Test1()
{
var sw
= Stopwatch.StartNew();
var instance
= new EventTest();
Thread thread
= new Thread(AddAndRemoveEventHandler);
thread.Start(instance);
int i = 0;
try
{
for (i = 0; i < 2000; i++)
{
if (instance.MyEvent != null)
{
instance.MyEvent(instance, EventArgs.Empty);
}
Thread.Sleep(
0);
}
Console.WriteLine();
}
catch (Exception exception)
{
sw.Stop();
Console.WriteLine();
Console.WriteLine(
"index = {0}, time: {1}", i, sw.Elapsed);
Console.WriteLine(exception);
}
finally
{
thread.Abort();
thread.Join();
}
}

static void Test2()
{
var sw
= Stopwatch.StartNew();
var instance
= new EventTest();
Thread thread
= new Thread(AddAndRemoveEventHandler);
thread.Start(instance);
int i = 0;
try
{
for (i = 0; i < 2000; i++)
{
EventHandler handler
= Interlocked.Exchange(ref instance.MyEvent, null);
if (handler != null)
{
handler(instance, EventArgs.Empty);
}
Thread.Sleep(
0);
}
Console.WriteLine();
}
catch (Exception exception)
{
sw.Stop();
Console.WriteLine();
Console.WriteLine(
"index = {0}, time: {1}", i, sw.Elapsed);
Console.WriteLine(exception);
}
finally
{
thread.Abort();
thread.Join();
}
}
}
}

  我测试了好几次,index最小的一次是60多,最大的1000多,并发问题还是比较明显的。下面是其中一次测试结果:

  有些人倾向于使用EventHandler handler=NewEvent;代替使用Interlocked.Exchange方法,书中也提到了,这种方式也是可行的,因为MS的JIT编译器不会将这里的handler优化掉。书中最后说道“另外由于事件主要在单线程的情形中使用(WinForm/WPF/SilverLight),所以线程安全并不是一个问题。”

  我认为,这个问题还是有必要注意一下的。这种问题一般都很难重现,而且还是该死的NullReferenceException异常,一看上下文代码,霎时间还真是“莫名其妙”,最后归于人品问题倒是相当无奈了。

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载