1 internal enum CoordinationStatus { AllDone, Timeout, Cancel }
2
3 /// <summary>
4 /// 协调所有异步操作
5 /// </summary>
6 internal sealed class AsyncCoordinator
7 {
8 //AllBegun内部调用JustEnded来递减
9 private int m_opCount = 1;
10 //0为false,1为true
11 private int m_statusReported = 0;
12
13 private Action<CoordinationStatus> m_callback;
14
15 private Timer m_timer;
16
17 /// <summary>
18 /// 该方法必须在发起一个操作之前调用
19 /// </summary>
20 /// <param name="opsToAdd"></param>
21 public void AboutToBegin(int opsToAdd = 1)
22 {
23 //为多个线程共享的变量提供原子操作
24 //对两个 32 位整数进行求和并用和替换第一个整数,上述操作作为一个原子操作完成
25 // 参数:
26 // location1:
27 // 一个变量,包含要添加的第一个值。 两个值的总和存储在 location1。
28 //
29 // value:
30 // 要添加到整数的值 location1。
31 //
32 // 返回结果:
33 // 新值存储在 location1。
34 Interlocked.Add(ref m_opCount, opsToAdd);
35 }
36
37 /// <summary>
38 /// 该方法必须在处理好一个操作的结果之后调用
39 /// </summary>
40 public void JustEnded()
41 {
42 //为多个线程共享的变量提供原子操作
43 //以原子操作的形式递减指定变量的值并存储结果
44 // 参数:
45 // location:
46 // 其值要递减的变量。
47 //
48 // 返回结果:
49 // 递减的值。
50 if (Interlocked.Decrement(ref m_opCount) == 0)
51 {
52 ReportStatus(CoordinationStatus.AllDone);
53 }
54 }
55
56 /// <summary>
57 /// 该方法必须在发起所有操作之后调用
58 /// </summary>
59 /// <param name="action"></param>
60 /// <param name="timeout"></param>
61 public void AllBegun(Action<CoordinationStatus> action, int timeout = Timeout.Infinite)
62 {
63 m_callback = action;
64
65 //若不是无限等待
66 if (timeout != Timeout.Infinite)
67 {
68 m_timer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
69 }
70
71 JustEnded();
72 }
73
74 /// <summary>
75 /// 取消
76 /// </summary>
77 public void Cancel()
78 {
79 ReportStatus(CoordinationStatus.Cancel);
80 }
81
82 /// <summary>
83 /// 超时
84 /// </summary>
85 /// <param name="obj"></param>
86 private void TimeExpired(object obj)
87 {
88 ReportStatus(CoordinationStatus.Timeout);
89 }
90
91 /// <summary>
92 /// 报告状态
93 /// </summary>
94 /// <param name="status"></param>
95 private void ReportStatus(CoordinationStatus status)
96 {
97 //如果状态从未报告过就报告它,否则忽略它
98 //为多个线程共享的变量提供原子操作
99 //以原子操作的形式,将 32 位有符号整数设置为指定的值并返回原始值
100 // 参数:
101 // location1:
102 // 要设置为指定值的变量。
103 //
104 // value:
105 // location1 参数要设置成的值。
106 //
107 // 返回结果:
108 // location1 的原始值。
109 if (Interlocked.Exchange(ref m_statusReported, 1) == 0)
110 {
111 m_callback(status);
112 }
113 }
114 }
115
116 internal sealed class MultiWebRequests
117 {
118 //用于协调所有异步操作
119 private AsyncCoordinator m_AsyncCoordinator = new AsyncCoordinator();
120
121 //想要查询的Web服务器及其响应(异常或int)的集合
122 //多个线程访问该字典时不需要同步进行,因为构造后键是只读的
123 private Dictionary<string, object> m_servers = new Dictionary<string, object>
124 {
125 {"http://referencesource.microsoft.com/",null },
126 {"https://msdn.microsoft.com/zh-CN/",null },
127 {"https://www.microsoft.com/net",null },
128 {"http://www.songtaste.com/",null }
129 };
130
131 public MultiWebRequests(int timeout = Timeout.Infinite)
132 {
133 //以异步方式,一次性发起所有请求
134 var httpClient = new HttpClient();
135 foreach (var server in m_servers.Keys)
136 {
137 m_AsyncCoordinator.AboutToBegin(1);
138 httpClient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));
139 }
140
141 //告诉AsyncCoordinator 所有操作均已发起
142 //并在所有操作完成或调用Cancel或超时时调用AllDone
143 m_AsyncCoordinator.AllBegun(AllDone, timeout);
144 }
145
146 private void ComputeResult(string server, Task<byte[]> task)
147 {
148 Object result;
149 if (task.Exception != null)
150 {
151 result = task.Exception.InnerException;
152 }
153 else
154 {
155 result = task.Result.Length;
156 }
157
158 //保存结果,指出1个操作完成
159 m_servers[server] = result;
160 m_AsyncCoordinator.JustEnded();
161 }
162
163 /// <summary>
164 /// 调用这个方法指出结果已无关紧要
165 /// </summary>
166 public void Cancel()
167 {
168 m_AsyncCoordinator.Cancel();
169 }
170
171 //所有服务器都响应、调用了Cancel或发生了超时就调用该方法
172 private void AllDone(CoordinationStatus status)
173 {
174 switch (status)
175 {
176 case CoordinationStatus.AllDone:
177 Console.WriteLine("Completed");
178 foreach (var server in m_servers)
179 {
180 Console.Write("{0} ", server.Key);
181 object result = server.Value;
182 if (result is Exception)
183 {
184 Console.WriteLine("Failed due to {0}.", result.GetType().Name);
185 }
186 else
187 {
188 Console.WriteLine("Returned {0:N0} bytes.", result);
189 }
190 }
191 break;
192 case CoordinationStatus.Timeout:
193 Console.WriteLine("Timeout");
194 break;
195 case CoordinationStatus.Cancel:
196 Console.WriteLine("Cancelled");
197 break;
198 default:
199 break;
200 }
201 }
202 }
m_opCount字段初始化为1(而非0),执行构造器方法的线程在发出Web服务器请求期间,由于m_opCount为1,所以能保证AllDone不会被调用,构造器调用AllBegun之前,m_opCount不可能变成0。构造器调用AllBegun时,AllBegun内部调用JustEnded来递减m_opCount,所以事实上撤销了把它初始化成1的效果,现在m_opCount能变成0了,但只能是在发起了所有Web服务器请求之后。