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服务器请求之后。