(转)对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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182


183

184

185

186

187

188

189

190

191

192


193

194

195

196

197

198

199

200

201

202

203

204

205

206

207


208

209

210

211

212

213

214

215

216

217

218
