(转)对Queue的同步操作,了解lock以及 AutoResetEvent 和 ManualResetEvent 类用法
代码如下:
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5using System.Threading;
6using System.Collections;
7
8
9namespace SyncEvents
10{
11 public class SyncEvents
12 {
13
14 public SyncEvents()
15 {
16 //AutoResetEvent 类用于“新项”事件,因为您希望每当使用者线程响应此事件后,此事件都能自动重置
17 _newItemEvent = new AutoResetEvent(false);
18
19 //ManualResetEvent 类用于“退出”事件,因为您希望当此事件终止时有多个线程响应
20 _exitThreadEvent = new ManualResetEvent(false);
21
22 //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。
23 _eventArray = new WaitHandle[2];
24
25 _eventArray[0] = _newItemEvent;
26
27 _eventArray[1] = _exitThreadEvent;
28
29 }
30
31 public EventWaitHandle ExitThreadEvent
32 {
33
34 get { return _exitThreadEvent; }
35
36 }
37
38 public EventWaitHandle NewItemEvent
39 {
40
41 get { return _newItemEvent; }
42
43 }
44
45 public WaitHandle[] EventArray
46 {
47
48 get { return _eventArray; }
49
50 }
51 //制造者线程用来在有新项添加到队列中时通知使用者线程
52 private EventWaitHandle _newItemEvent;
53 //用来通知辅助线程终止
54 private EventWaitHandle _exitThreadEvent;
55 //这两个事件对象封装在一个名为 SyncEvents 的类中。这使事件可以轻松传递到表示制造者线程和使用者线程的对象。
56 private WaitHandle[] _eventArray;
57
58 }
59 /// <summary>
60 /// 制造者线程
61 /// </summary>
62 public class Producer
63 {
64
65 public Producer(Queue<int> q, SyncEvents e)
66 {
67
68 _queue = q;
69
70 _syncEvents = e;
71
72 }
73
74 public void ThreadRun()
75 {
76
77 int count = 0;
78
79 Random r = new Random();
80 //WaitOne 使用的第一个参数为零,这表示该方法应立即返回。
81 while (!_syncEvents.ExitThreadEvent.WaitOne(0, false))
82 {
83 //在添加新项前,该集合必须处于锁定状态,以防止使用者线程和主线程同时访问该集合。ICollection 接口公开的 SyncRoot 字段。此字段专门为同步线程访问而提供
84 lock (((ICollection)_queue).SyncRoot)
85 {
86
87 while (_queue.Count < 20)
88 {
89
90 _queue.Enqueue(r.Next(0, 100));
91
92 //对于制造者添加到队列中的每个新项,都将调用“新项”事件的 Set 方法。这将通知使用者线程离开挂起状态并开始处理新项。
93 _syncEvents.NewItemEvent.Set();
94
95 count++;
96 }
97 }
98 }
99
100 Console.WriteLine("Producer thread: produced {0} items", count);
101 }
102
103 private Queue<int> _queue;
104
105 private SyncEvents _syncEvents;
106
107 }
108 /// <summary>
109 /// 使用者线程
110 /// </summary>
111 public class Consumer
112 {
113
114 public Consumer(Queue<int> q, SyncEvents e)
115 {
116
117 _queue = q;
118
119 _syncEvents = e;
120
121 }
122
123 public void ThreadRun()
124 {
125
126 int count = 0;
127 //使用 WaitAny 来阻止使用者线程,直到所提供的数组中的任意一个等待句柄变为终止状态
128 //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。数组中有两个句柄,一个用来终止辅助线程,另一个用来指示有新项添加到集合中
129 while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
130 {
131
132 lock (((ICollection)_queue).SyncRoot)
133 {
134
135 int item = _queue.Dequeue();
136
137 }
138
139 count++;
140
141 }
142
143 Console.WriteLine("Consumer Thread: consumed {0} items", count);
144
145 }
146
147 private Queue<int> _queue;
148
149 private SyncEvents _syncEvents;
150
151 }
152 /// <summary>
153 /// 该示例创建两个辅助线程。一个线程生成元素并将它们存储在非线程安全的泛型队列中。有关更多信息,请参见 Queue。另一个线程使用此队列中的项。另外,主线程定期显示队列的内容,因此该队列被三个线程访问。lock 关键字用于同步对队列的访问,以确保队列的状态没有被破坏。
154 /// </summary>
155 public class ThreadSyncSample
156 {
157 private static void ShowQueueContents(Queue<int> q)
158 {
159 //ShowQueueContents 是由主线程执行的,因为它被 Main 调用。这意味着当此方法获得对项队列的独占访问权限时,它实际上既阻止了制造者线程访问队列,也阻止了使用者线程访问队列。
160 lock (((ICollection)q).SyncRoot)
161 {
162
163 foreach (int item in q)
164 {
165
166 Console.Write("{0} ", item);
167
168 }
169 }
170
171 Console.WriteLine();
172
173 }
174
175 static void Main()
176 {
177
178 Queue<int> queue = new Queue<int>();
179
180 SyncEvents syncEvents = new SyncEvents();
181
182 Console.WriteLine("Configuring worker threads");
183
184 Producer producer = new Producer(queue, syncEvents);
185
186 Consumer consumer = new Consumer(queue, syncEvents);
187
188 Thread producerThread = new Thread(producer.ThreadRun);
189
190 Thread consumerThread = new Thread(consumer.ThreadRun);
191
192 Console.WriteLine("Launching producer and consumer threads");
193 //创建了两个新的辅助线程,它们独立于当前正在执行 Main 方法的主线程开始异步执行过程
194 producerThread.Start();
195
196 consumerThread.Start();
197 //Main 接下来要做的事情是通过调用 Sleep 方法将主线程挂起。该方法将当前正在执行的线程挂起指定的时间(毫秒)。在此时间间隔过后,Main 将重新激活,这时它将显示队列的内容。Main 重复此过程四次
198 for (int i = 0; i < 4; i++)
199 {
200
201 Thread.Sleep(2500);
202
203 ShowQueueContents(queue);
204
205 }
206
207 Console.WriteLine("Signaling threads to terminate");
208 //Main 通过调用“退出线程”事件的 Set 方法通知辅助线程终止,然后对每个辅助线程调用 Join 方法以阻止主线程,直到每个辅助线程都响应该事件并终止。
209 syncEvents.ExitThreadEvent.Set();
210
211 producerThread.Join();
212
213 consumerThread.Join();
214
215 }
216 }
217}
218
2using System.Collections.Generic;
3using System.Text;
4
5using System.Threading;
6using System.Collections;
7
8
9namespace SyncEvents
10{
11 public class SyncEvents
12 {
13
14 public SyncEvents()
15 {
16 //AutoResetEvent 类用于“新项”事件,因为您希望每当使用者线程响应此事件后,此事件都能自动重置
17 _newItemEvent = new AutoResetEvent(false);
18
19 //ManualResetEvent 类用于“退出”事件,因为您希望当此事件终止时有多个线程响应
20 _exitThreadEvent = new ManualResetEvent(false);
21
22 //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。
23 _eventArray = new WaitHandle[2];
24
25 _eventArray[0] = _newItemEvent;
26
27 _eventArray[1] = _exitThreadEvent;
28
29 }
30
31 public EventWaitHandle ExitThreadEvent
32 {
33
34 get { return _exitThreadEvent; }
35
36 }
37
38 public EventWaitHandle NewItemEvent
39 {
40
41 get { return _newItemEvent; }
42
43 }
44
45 public WaitHandle[] EventArray
46 {
47
48 get { return _eventArray; }
49
50 }
51 //制造者线程用来在有新项添加到队列中时通知使用者线程
52 private EventWaitHandle _newItemEvent;
53 //用来通知辅助线程终止
54 private EventWaitHandle _exitThreadEvent;
55 //这两个事件对象封装在一个名为 SyncEvents 的类中。这使事件可以轻松传递到表示制造者线程和使用者线程的对象。
56 private WaitHandle[] _eventArray;
57
58 }
59 /// <summary>
60 /// 制造者线程
61 /// </summary>
62 public class Producer
63 {
64
65 public Producer(Queue<int> q, SyncEvents e)
66 {
67
68 _queue = q;
69
70 _syncEvents = e;
71
72 }
73
74 public void ThreadRun()
75 {
76
77 int count = 0;
78
79 Random r = new Random();
80 //WaitOne 使用的第一个参数为零,这表示该方法应立即返回。
81 while (!_syncEvents.ExitThreadEvent.WaitOne(0, false))
82 {
83 //在添加新项前,该集合必须处于锁定状态,以防止使用者线程和主线程同时访问该集合。ICollection 接口公开的 SyncRoot 字段。此字段专门为同步线程访问而提供
84 lock (((ICollection)_queue).SyncRoot)
85 {
86
87 while (_queue.Count < 20)
88 {
89
90 _queue.Enqueue(r.Next(0, 100));
91
92 //对于制造者添加到队列中的每个新项,都将调用“新项”事件的 Set 方法。这将通知使用者线程离开挂起状态并开始处理新项。
93 _syncEvents.NewItemEvent.Set();
94
95 count++;
96 }
97 }
98 }
99
100 Console.WriteLine("Producer thread: produced {0} items", count);
101 }
102
103 private Queue<int> _queue;
104
105 private SyncEvents _syncEvents;
106
107 }
108 /// <summary>
109 /// 使用者线程
110 /// </summary>
111 public class Consumer
112 {
113
114 public Consumer(Queue<int> q, SyncEvents e)
115 {
116
117 _queue = q;
118
119 _syncEvents = e;
120
121 }
122
123 public void ThreadRun()
124 {
125
126 int count = 0;
127 //使用 WaitAny 来阻止使用者线程,直到所提供的数组中的任意一个等待句柄变为终止状态
128 //此数组是必需的,因为它使使用者线程可以响应两个事件中的任何一个。数组中有两个句柄,一个用来终止辅助线程,另一个用来指示有新项添加到集合中
129 while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
130 {
131
132 lock (((ICollection)_queue).SyncRoot)
133 {
134
135 int item = _queue.Dequeue();
136
137 }
138
139 count++;
140
141 }
142
143 Console.WriteLine("Consumer Thread: consumed {0} items", count);
144
145 }
146
147 private Queue<int> _queue;
148
149 private SyncEvents _syncEvents;
150
151 }
152 /// <summary>
153 /// 该示例创建两个辅助线程。一个线程生成元素并将它们存储在非线程安全的泛型队列中。有关更多信息,请参见 Queue。另一个线程使用此队列中的项。另外,主线程定期显示队列的内容,因此该队列被三个线程访问。lock 关键字用于同步对队列的访问,以确保队列的状态没有被破坏。
154 /// </summary>
155 public class ThreadSyncSample
156 {
157 private static void ShowQueueContents(Queue<int> q)
158 {
159 //ShowQueueContents 是由主线程执行的,因为它被 Main 调用。这意味着当此方法获得对项队列的独占访问权限时,它实际上既阻止了制造者线程访问队列,也阻止了使用者线程访问队列。
160 lock (((ICollection)q).SyncRoot)
161 {
162
163 foreach (int item in q)
164 {
165
166 Console.Write("{0} ", item);
167
168 }
169 }
170
171 Console.WriteLine();
172
173 }
174
175 static void Main()
176 {
177
178 Queue<int> queue = new Queue<int>();
179
180 SyncEvents syncEvents = new SyncEvents();
181
182 Console.WriteLine("Configuring worker threads");
183
184 Producer producer = new Producer(queue, syncEvents);
185
186 Consumer consumer = new Consumer(queue, syncEvents);
187
188 Thread producerThread = new Thread(producer.ThreadRun);
189
190 Thread consumerThread = new Thread(consumer.ThreadRun);
191
192 Console.WriteLine("Launching producer and consumer threads");
193 //创建了两个新的辅助线程,它们独立于当前正在执行 Main 方法的主线程开始异步执行过程
194 producerThread.Start();
195
196 consumerThread.Start();
197 //Main 接下来要做的事情是通过调用 Sleep 方法将主线程挂起。该方法将当前正在执行的线程挂起指定的时间(毫秒)。在此时间间隔过后,Main 将重新激活,这时它将显示队列的内容。Main 重复此过程四次
198 for (int i = 0; i < 4; i++)
199 {
200
201 Thread.Sleep(2500);
202
203 ShowQueueContents(queue);
204
205 }
206
207 Console.WriteLine("Signaling threads to terminate");
208 //Main 通过调用“退出线程”事件的 Set 方法通知辅助线程终止,然后对每个辅助线程调用 Join 方法以阻止主线程,直到每个辅助线程都响应该事件并终止。
209 syncEvents.ExitThreadEvent.Set();
210
211 producerThread.Join();
212
213 consumerThread.Join();
214
215 }
216 }
217}
218