(转)对Queue的同步操作,了解lock以及 AutoResetEvent 和 ManualResetEvent 类用法
代码如下:
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
using System.Threading;
6
using System.Collections;
7
8
9
namespace 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
using System;2
using System.Collections.Generic;3
using System.Text;4

5
using System.Threading;6
using System.Collections;7

8

9
namespace SyncEvents10
{11
public class SyncEvents12
{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 ExitThreadEvent32
{33

34
get { return _exitThreadEvent; }35

36
}37

38
public EventWaitHandle NewItemEvent39
{40

41
get { return _newItemEvent; }42

43
}44

45
public WaitHandle[] EventArray46
{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 Producer63
{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 Consumer112
{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 ThreadSyncSample156
{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


浙公网安备 33010602011771号