您的位置:首页 > 其它

线程安全的方式引发事件

2015-08-24 16:03 603 查看
消息发送Helper类(演示用,无任何意义):

public class MessageHelper
{
/// <summary>
/// 消息发送完成 触发的事件
/// </summary>
public event EventHandler<MessageEventArgs> Completed;

protected virtual void OnCompleted(MessageEventArgs e)
{
//一般情况都这样触发
if (Completed != null)
Completed(this, e);

//或者这样触发(不过编译器可能会做优化,和上面代码没什么区别)
//var temp = Completed;
//if (temp != null) temp(this, e);
}

/// <summary>
/// 发送消息
/// </summary>
/// <param name="from">发送人</param>
/// <param name="to">接收人</param>
/// <param name="content">消息内容</param>
public virtual void Send(string from, string to, string content)
{
Console.WriteLine(string.Format("{0} Send To {1},Message:{2}", from, to, content));

OnCompleted(new MessageEventArgs(from, to, content));
}
}

public class MessageEventArgs : EventArgs
{
public MessageEventArgs(string from, string to, string content)
{
this.From = from;
this.To = to;
this.Content = content;
}

public string From { get; set; }
public string To { get; set; }

public string Content { get; set; }
}


调用,消息发送完毕后调用OnCompleted()方法,这在单线程下没有任何问题:

class Program
{
static void Main(string[] args)
{
MessageHelper message = new MessageHelper();

message.Completed += message_Completed;

message.Send("习大大", "小泉", "小子,轻点嘚瑟!");

Console.ReadKey();
}

static void message_Completed(object sender, MessageEventArgs e)
{
Console.WriteLine("消息发送完毕!");
}
}


但如果在多线程下对 Completed事件操作时会问题。

为了模拟多线程下的并发,对代码进行了修改:

static void Main(string[] args)
{
MessageHelper message = new MessageHelper();

message.Completed += message_Completed;

Thread thread1 = new Thread(() =>
{
message.Send("习大大", "小泉", "小子,轻点嘚瑟!");
});

//这个线程只是 移除了事件
Thread thread2 = new Thread(() =>
{
Thread.Sleep(1000);
message.Completed -= message_Completed;
});

thread1.Start();
thread2.Start();
//message.Send("习大大", "小泉", "小子,轻点嘚瑟!");

Console.ReadKey();
}

static void message_Completed(object sender, MessageEventArgs e)
{
Console.WriteLine("消息发送完毕!");
}


protected virtual void OnCompleted(MessageEventArgs e)
{
//一般情况都这样触发
if (Completed != null)
{
Thread.Sleep(2000); //这里是为了等待Completed被移除
Completed(this, e);
}

//或者这样触发(不过编译器可能会做优化,和上面代码没什么区别)
//var temp = Completed;
//if (temp != null) temp(this, e);
}


这时再运行就会出现Object reference not set to an instance of an object 异常。

所以应该以线程安全的方式触发事件,这里用到了Interlocked 类的泛型方法:



protected virtual void OnCompleted(MessageEventArgs e)
{
////一般情况都这样触发
//if (Completed != null)
//{
//    Thread.Sleep(2000); //这里是为了等待Completed被移除
//    Completed(this, e);
//}

//或者这样触发(不过编译器可能会做优化,和上面代码没什么区别)
//var temp = Completed;
//if (temp != null) temp(this, e);

//线程安全的方式触发事件
EventHandler<MessageEventArgs> temp = Interlocked.CompareExchange<EventHandler<MessageEventArgs>>(ref Completed, null, null);
Thread.Sleep(2000);//这里是为了等待Completed被移除
temp(this, e);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: