代码改变世界

实战(代码)练习 C# 多线程

2011-12-22 17:15  音乐让我说  阅读(555)  评论(0编辑  收藏  举报

所有说明在代码中。

    public class MessageReceivedEventArgs : EventArgs
{
public string Message
{
get;
set;
}

public MessageReceivedEventArgs(string message)
{
this.Message = message;
}
}

 

上面定义了一个继承自 EventArgs 的类,用来做事件的参数。

    public class UnsafeMessenger
{
/// <summary>
/// 当信息发送后触发
/// </summary>
public event EventHandler<MessageReceivedEventArgs> OnMessageReceived;

public void SendMessage(object message)
{
if(!(message is string))
{
throw new ArgumentException("必须传入 String 类型的消息");
}
SendMessage((string)message);
}

/// <summary>
/// 发送信息
/// </summary>
/// <param name="message"></param>
public void SendMessage(string message)
{
Thread.Sleep(2000); // 模拟一个耗时的发送消息的过程
Console.WriteLine("==========消息发送成功!==========");
var target = OnMessageReceived;
// 你可能好奇:为什么要用一个变量来接收一下。解释如下:
// 因为在多线程中,客户端既然可以订阅事件,也可以取消订阅。
// 那如果客户端刚开始订阅了,后来又取消了,就容易发生异常。
// 比如:
// if(OnMessageReceived != null)
// {
// OnMessageReceived(this, new MessageReceivedEventArgs(message));
// }
// 上面的代码就很可能发生空指针异常,在多线程环境下。
// 总的原因就是:委托的不可变性,就像 string 一样。
// 当委托在 Add 或 Remove 后,就会形成一个新的委托。
if (target != null)
{
Thread.Sleep(4000);
target(this, new MessageReceivedEventArgs(message));
}
}
}

 

上面我们定义了一个 UnsafeMessenger 类用来发送消息,那为什么要加一个事件呢,因为客户端可能需要在发送完消息后,再做一些其他的事。

    class Program
{
static void Main(string[] args)
{
string objMessage = "你们好,欢迎来到 .NET 的世界!";

UnsafeMessenger unsafeMsg = new UnsafeMessenger();
unsafeMsg.OnMessageReceived += new EventHandler<MessageReceivedEventArgs>(OnMessageReceived1);
unsafeMsg.OnMessageReceived += new EventHandler<MessageReceivedEventArgs>(OnMessageReceived2);

Thread childThread = new Thread(unsafeMsg.SendMessage);
childThread.Name = "运行发送消息的子线程";
childThread.IsBackground = false; // 不用设置也可以,默认为 false
// 什么是后台线程,我的理解是:
// 后台线程就是在后台运行的线程,主线程不会等待子线程执行完毕。

Console.WriteLine("当前子线程的状态:" + childThread.ThreadState);
Console.WriteLine("当前子线程是否属于托管线程池:" + childThread.IsThreadPoolThread);
childThread.Start(objMessage);

Thread.Sleep(3000);

unsafeMsg.OnMessageReceived -= new EventHandler<MessageReceivedEventArgs>(OnMessageReceived1);
unsafeMsg.OnMessageReceived -= new EventHandler<MessageReceivedEventArgs>(OnMessageReceived2);

Console.WriteLine("主线程运行完毕!");
}

static void OnMessageReceived1(object sender, MessageReceivedEventArgs e)
{
Thread.Sleep(3000); // 假设耗时 3 秒钟
Console.WriteLine("\n\n");
Console.WriteLine("当前线程的名称:" + Thread.CurrentThread.Name);
Console.WriteLine("消息发送成功后,调用了本回调函数(OnMessageReceived1)。消息:" + e.Message + "\n");
}

static void OnMessageReceived2(object sender, MessageReceivedEventArgs e)
{
Thread.Sleep(5000); // 假设耗时 5 秒钟
Console.WriteLine("\n\n");
Console.WriteLine("当前线程的名称:" + Thread.CurrentThread.Name);
Console.WriteLine("消息发送成功后,调用了本回调函数(OnMessageReceived2)。消息:" + e.Message + "\n");

}
}



运行结果截图:

PS:发一张 CSDN 数据库泄露后,我的查询结果的图。

谢谢浏览!