在前一篇中,还存在一个问题,当没有请求进来时,线程仍然会轮询执行,这样造成了CPU资源的浪费,因此,需要进一步改进,实现仅当有请求进入时,才启动线程。
还是先实现协程容器
1 /// <summary> 2 /// 基于自动多线程的协程容器实现 3 /// </summary> 4 public class CoroutineContainerMultipleAuto : ICoroutineContainer 5 { 6 7 /// <summary> 8 /// 存储多线程协程单元项 9 /// </summary> 10 private List<MultipleItem> _multipleItems = new List<MultipleItem>(); 11 12 /// <summary> 13 /// 设定的线程数 14 /// </summary> 15 private int _threadCount; 16 /// <summary> 17 /// 错误处理 18 /// </summary> 19 private Action<ICoroutineUnit,Exception> _errorHandle; 20 public CoroutineContainerMultipleAuto(int threadCount,Action<ICoroutineUnit,Exception> errorHandle) 21 { 22 _threadCount = threadCount; 23 _errorHandle = errorHandle; 24 } 25 public void Register(ICoroutineUnit unit) 26 { 27 //随机分摊请求 28 Random random = new Random(); 29 var index = random.Next(0, _threadCount); 30 31 lock (_multipleItems[index].AddUnits) 32 { 33 _multipleItems[index].AddUnits.Add(new UnitItem() { Unit = unit, UnitResult = null }); 34 //要求协程处理线程启动 35 _multipleItems[index].Run(); 36 } 37 38 } 39 /// <summary> 40 /// 容器启动 41 /// 这里并不会真正启动线程 42 /// </summary> 43 public void Run() 44 { 45 for (var index = 0; index <= _threadCount - 1; index++) 46 { 47 var multipleItem = new MultipleItem() { Units = new List<UnitItem>(), AddUnits = new List<UnitItem>(), ErrorHandle=_errorHandle }; 48 _multipleItems.Add(multipleItem); 49 50 } 51 52 53 54 } 55 56 57 private class UnitItem 58 { 59 public ICoroutineUnit Unit { get; set; } 60 public IEnumerator<Task> UnitResult { get; set; } 61 } 62 63 /// <summary> 64 /// 多线程协程单元项 65 /// </summary> 66 private class MultipleItem 67 { 68 public List<UnitItem> Units 69 { 70 get; set; 71 } 72 73 public List<UnitItem> AddUnits 74 { 75 get; set; 76 } 77 78 public Action<ICoroutineUnit,Exception> ErrorHandle { get; set; } 79 80 81 private bool _running = false; 82 private bool _continue = true; 83 84 /// <summary> 85 /// 仅当外部调用方调用时才启动 86 /// 同时保证只有一个线程启动 87 /// </summary> 88 public void Run() 89 { 90 lock (this) 91 { 92 if (_running) 93 { 94 _continue = true; 95 } 96 else 97 { 98 _continue = false; 99 } 100 101 } 102 103 if (!_running) 104 { 105 lock (this) 106 { 107 _running = true; 108 } 109 110 Task.Run(() => 111 { 112 113 114 while (true) 115 { 116 lock (this) 117 { 118 _continue = false; 119 } 120 121 while (true) 122 { 123 lock (AddUnits) 124 { 125 foreach (var addItem in AddUnits) 126 { 127 Units.Add(addItem); 128 } 129 AddUnits.Clear(); 130 } 131 132 133 if (Units.Count == 0) 134 { 135 break; 136 } 137 138 foreach (var item in Units) 139 { 140 if (item.UnitResult == null) 141 { 142 var result = item.Unit.Do(); 143 144 try 145 { 146 result.MoveNext(); 147 } 148 catch (Exception ex) 149 { 150 151 ErrorHandle(item.Unit, ex); 152 153 Units.Remove(item); 154 155 break; 156 } 157 158 item.UnitResult = result; 159 } 160 else 161 { 162 if (item.UnitResult.Current.IsCanceled || item.UnitResult.Current.IsCompleted || item.UnitResult.Current.IsFaulted) 163 { 164 var nextResult = true; 165 try 166 { 167 nextResult = item.UnitResult.MoveNext(); 168 } 169 catch (Exception ex) 170 { 171 ErrorHandle(item.Unit, ex); 172 173 Units.Remove(item); 174 175 break; 176 } 177 if (!nextResult) 178 { 179 180 Units.Remove(item); 181 182 break; 183 } 184 } 185 } 186 } 187 } 188 189 190 lock (this) 191 { 192 if (!_continue) 193 { 194 _running = false; 195 break; 196 } 197 } 198 } 199 200 201 202 }); 203 204 } 205 206 207 } 208 209 210 211 212 } 213 }
主程序调用
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //错误处理 6 Action<ICoroutineUnit, Exception> errorHandle = (unit, ex) => 7 { 8 var response = ((HttpAction)unit).Response; 9 response.ContentType = "text/html"; 10 string str = "访问出错,错误原因:" + ex.ToString(); 11 var bytes = UTF8Encoding.UTF8.GetBytes(str); 12 response.OutputStream.Write(bytes, 0, bytes.Length); 13 response.OutputStream.Close(); 14 }; 15 //容器初始化 16 ICoroutineContainer coroutineContainerMultipleAuto = new CoroutineContainerMultipleAuto(2, errorHandle); 17 coroutineContainerMultipleAuto.Run(); 18 19 //监听 20 HttpListener listener = new HttpListener(); 21 22 listener.Start(); 23 listener.Prefixes.Add("http://localhost:8000/"); 24 25 while (true) 26 { 27 var httpContext = listener.GetContext(); 28 //创建Http请求的协程单元 29 HttpAction action = new HttpAction(httpContext.Request, httpContext.Response); 30 //注册分发协程单元 31 coroutineContainerMultipleAuto.Register(action); 32 } 33 34 35 } 36 37 38 }
程序执行后,
打开浏览器访问,获得结果
至此,基于自动多线程的协程简单框架完成