转载自 http://blog.csdn.net/livelylittlefish/article/details/2735440
本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一、小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!
C#读写者线程(用AutoResetEvent实现同步)
1. AutoResetEvent简介
通知正在等待的线程已发生事件。无法继承此类。
常用方法简介:
false:无信号,子线程的WaitOne方法不会被自动调用 true:有信号,子线程的WaitOne方法会被自动调用
对于具有 EventResetMode.AutoReset(包括 AutoResetEvent)的 EventWaitHandle,Set 方法释放单个线程。如果没有等待线程,等待句柄将一直保持终止状态,直到某个线程尝试等待它,或者直到它的 Reset 方法被调用。
对于具有 EventResetMode.ManualReset(包括 ManualResetEvent)的 EventWaitHandle,调用Set 方法将使等待句柄一直保持终止状态,直到它的 Reset 方法被调用。
当在派生类中重写时,阻止当前线程,直到当前的 WaitHandle 收到信号。
2. 读写者线程例子
本例子中,主线程作为写线程,要对某个数据(本例中是个变量)赋值(即写动作),而读线程则等待写线程每次写完数据发出通知,待读线程收到通知后,将数据读出并显示。
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace TestAutoResetEvent { /// /// 读写者线程 /// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出 /// class Program { //写线程将数据写入myData static int myData = 100; //读写次数 const int readWriteCount = 10; //false:初始时没有信号 static AutoResetEvent autoResetEvent = new AutoResetEvent(false); static void Main(string[] args) { //开启一个读线程(子线程) Thread readerThread = new Thread(new ThreadStart(ReadThreadProc)); readerThread.Name = "ReaderThread"; readerThread.Start(); for (int i = 1; i <= readWriteCount; i++) { Console.WriteLine("MainThread writing : {0}", i); //主(写)线程将数据写入 myData = i; //主(写)线程发信号,说明值已写过了 //即通知正在等待的线程有事件发生 autoResetEvent.Set(); Thread.Sleep(0); } //终止线程 readerThread.Abort(); } static void ReadThreadProc() { while (true) { //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号) autoResetEvent.WaitOne(); Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData); } } } }
运行结果如下:
由运行结果可以看出,写线程写入的数据有丢失,主要原因是写线程没有给读线程留足够的时间去进行读操作。
3. 对1进行修改
将主线程睡眠时间改为非0值,观察运行结果。
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace TestAutoResetEvent { /// /// 读写者线程 /// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出 /// class Program { //写线程将数据写入myData static int myData = 100; //读写次数 const int readWriteCount = 10; //false:初始时没有信号 static AutoResetEvent autoResetEvent = new AutoResetEvent(false); static void Main(string[] args) { //开启一个读线程(子线程) Thread readerThread = new Thread(new ThreadStart(ReadThreadProc)); readerThread.Name = "ReaderThread"; readerThread.Start(); for (int i = 1; i <= readWriteCount; i++) { Console.WriteLine("MainThread writing : {0}", i); //主(写)线程将数据写入 myData = i; //主(写)线程发信号,说明值已写过了 //即通知正在等待的线程有事件发生 autoResetEvent.Set(); Thread.Sleep(1); } //终止线程 readerThread.Abort(); } static void ReadThreadProc() { while (true) { //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号) autoResetEvent.WaitOne(); Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData); } } } }
运行结果如下:
有结果可知,当主线程睡眠时间大于0值时,读线程即有足够的时间读取写线程写入的数据。这个睡眠时间的长短可以根据实际应用中子线程的计算量设定。
4. 对1再进行修改
主线程在写完数据后根本不睡吗呢?这个时候会发生什么事情?
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace TestAutoResetEvent { /// /// 读写者线程 /// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出 /// class Program { //写线程将数据写入myData static int myData = 100; //读写次数 const int readWriteCount = 10; //false:初始时没有信号 static AutoResetEvent autoResetEvent = new AutoResetEvent(false); static void Main(string[] args) { //开启一个读线程(子线程) Thread readerThread = new Thread(new ThreadStart(ReadThreadProc)); readerThread.Name = "ReaderThread"; readerThread.Start(); for (int i = 1; i <= readWriteCount; i++) { Console.WriteLine("MainThread writing : {0}", i); //主(写)线程将数据写入 myData = i; //主(写)线程发信号,说明值已写过了 //即通知正在等待的线程有事件发生 autoResetEvent.Set(); //Thread.Sleep(1); } //终止线程 readerThread.Abort(); } static void ReadThreadProc() { while (true) { //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号) autoResetEvent.WaitOne(); Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData); } } } }
运行结果如下:
有结果可知,不睡眠的情况和睡眠时间为0(即Thread.Sleep(0);)效果产不多,只是不睡眠丢失的数据更多了。
5. 对1再修改
将传递给AutoResetEvent的构造函数的参数设置为true,观察运行结果。
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace TestAutoResetEvent { /// /// 读写者线程 /// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出 /// class Program { //写线程将数据写入myData static int myData = 100; //读写次数 const int readWriteCount = 10; //false:初始时没有信号 static AutoResetEvent autoResetEvent = new AutoResetEvent(true); static void Main(string[] args) { //开启一个读线程(子线程) Thread readerThread = new Thread(new ThreadStart(ReadThreadProc)); readerThread.Name = "ReaderThread"; readerThread.Start(); for (int i = 1; i <= readWriteCount; i++) { Console.WriteLine("MainThread writing : {0}", i); //主(写)线程将数据写入 myData = i; //主(写)线程发信号,说明值已写过了 //即通知正在等待的线程有事件发生 autoResetEvent.Set(); Thread.Sleep(0); } //终止线程 readerThread.Abort(); } static void ReadThreadProc() { while (true) { //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号) autoResetEvent.WaitOne(); Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData); } } } }
运行结果如下:
若将主线程的睡眠时间改为任意非0值,其运行结果均为下图所示的结果。
6. 其他修改
将主线程调用AutoResetEvent对象的Set方法删除,分别对AutoResetEvent的构造函数的参数为false和true观察运行结果。
为false,运行结果如下图所示。
为true,运行结果如下图所示。
至此,我想我们应该明白AutoResetEvent构造函数的参数的意义了。 false:无信号,子线程的WaitOne方法不会被自动调用; true:有信号,子线程的WaitOne方法会被自动调用。