0
|
1 ???using System; |
|
2 using System.Threading; |
|
3 using System.Collections.Generic; |
|
4 |
|
5 namespace UCIS { |
|
6 public class ThreadPool { |
|
7 private static ThreadPool pManager = null; |
|
8 |
|
9 public static ThreadPool DefaultPool { |
|
10 get { |
|
11 if (pManager == null) pManager = new ThreadPool(); |
|
12 return pManager; |
|
13 } |
|
14 } |
|
15 |
|
16 //Starts a long-term background task |
|
17 public static WorkItem RunTask(WaitCallback Callback, object State) { |
|
18 return DefaultPool.QueueWorkItem(Callback, State); |
|
19 } |
|
20 //Starts a short-term background task |
|
21 public static WorkItem RunCall(WaitCallback Callback, object State) { |
|
22 return DefaultPool.QueueWorkItem(Callback, State); |
|
23 } |
|
24 |
|
25 |
|
26 public class WorkItem { |
|
27 public WaitCallback Callback { get; internal set; } |
|
28 public object State { get; internal set; } |
|
29 public ThreadInfo Thread { get; internal set; } |
|
30 } |
|
31 public class ThreadInfo { |
|
32 public Thread Thread { get; internal set; } |
|
33 internal AutoResetEvent WaitHandle = new AutoResetEvent(false); |
|
34 public WorkItem WorkItem { get; internal set; } |
|
35 public bool Busy { get; internal set; } |
|
36 public bool Abort { get; internal set; } |
|
37 public DateTime LastActive { get; internal set; } |
|
38 } |
|
39 |
|
40 public class ExceptionEventArgs : EventArgs { |
|
41 public ExceptionEventArgs(WorkItem Item, Exception Exception, bool ThrowError) { |
|
42 this.Item = Item; |
|
43 this.Exception = Exception; |
|
44 this.ThrowError = ThrowError; |
|
45 } |
|
46 public WorkItem Item; |
|
47 public Exception Exception; |
|
48 public bool ThrowError; |
|
49 } |
|
50 |
|
51 private List<ThreadInfo> pThreads = new List<ThreadInfo>(); |
|
52 private List<ThreadInfo> pBusyThreads = new List<ThreadInfo>(); |
|
53 private Queue<ThreadInfo> pIdleThreads = new Queue<ThreadInfo>(); |
|
54 private int pThreadsMax; |
|
55 private int pThreadsMinIdle; |
|
56 private int pThreadsMaxIdle; |
|
57 |
|
58 public event OnExceptionEventHandler OnException; |
|
59 public delegate void OnExceptionEventHandler(ThreadPool sender, ExceptionEventArgs e); |
|
60 |
|
61 public System.Collections.ObjectModel.ReadOnlyCollection<ThreadInfo> Threads { |
|
62 get { |
|
63 return new System.Collections.ObjectModel.ReadOnlyCollection<ThreadInfo>(pThreads); |
|
64 } |
|
65 } |
|
66 |
|
67 public ThreadPool() : this(250, 1, 5) { } |
|
68 |
|
69 public ThreadPool(int MaxThreads, int MinIdle, int MaxIdle) { |
|
70 int I = 0; |
|
71 if (MaxThreads < 0) { |
|
72 throw new ArgumentOutOfRangeException("ThreadsMaxIdle", "ThreadsMaxIdle must greater than 0"); |
|
73 } else if (MaxThreads < MaxIdle) { |
|
74 throw new ArgumentOutOfRangeException("ThreadsMax", "ThreadsMax must be greater than or equal to ThreadsMaxIdle"); |
|
75 } else if (MaxIdle < 0) { |
|
76 throw new ArgumentOutOfRangeException("ThreadsMaxIdle", "ThreadsMaxIdle must greater than or equal to 0"); |
|
77 } else if (MinIdle < 0) { |
|
78 throw new ArgumentOutOfRangeException("ThreadsMinIdle", "ThreadsMinIdle must greater than or equal to 0"); |
|
79 } else if (MinIdle > MaxIdle) { |
|
80 throw new ArgumentOutOfRangeException("ThreadsMaxIdle", "ThreadsMaxIdle must be greater than or equal to ThreadsMinIdle"); |
|
81 } |
|
82 pThreadsMax = MaxThreads; |
|
83 pThreadsMinIdle = MinIdle; |
|
84 pThreadsMaxIdle = MaxIdle; |
|
85 for (I = 1; I <= pThreadsMinIdle; I++) { |
|
86 StartThread(false); |
|
87 } |
|
88 } |
|
89 |
|
90 public int ThreadsIdle { |
|
91 get { return pIdleThreads.Count; } |
|
92 } |
|
93 public int ThreadsBusy { |
|
94 get { return pBusyThreads.Count; } |
|
95 } |
|
96 public int ThreadsAlive { |
|
97 get { return pThreads.Count; } |
|
98 } |
|
99 public int ThreadsMinIdle { |
|
100 get { return pThreadsMinIdle; } |
|
101 set { |
|
102 if (value > pThreadsMaxIdle) { |
|
103 throw new ArgumentOutOfRangeException("ThreadsMinIdle", "ThreadsMinIdle must be smaller than ThreadsMaxIdle"); |
|
104 } else if (value < 0) { |
|
105 throw new ArgumentOutOfRangeException("ThreadsMinIdle", "ThreadsMinIdle must greater than or equal to 0"); |
|
106 } else { |
|
107 int I = 0; |
|
108 int C = 0; |
|
109 C = pIdleThreads.Count; |
|
110 if (value > C) { |
|
111 for (I = C; I <= value - 1; I++) { |
|
112 StartThread(false); |
|
113 } |
|
114 } |
|
115 pThreadsMinIdle = value; |
|
116 } |
|
117 } |
|
118 } |
|
119 public int ThreadsMaxIdle { |
|
120 get { return pThreadsMaxIdle; } |
|
121 set { |
|
122 if (pThreadsMinIdle > value) { |
|
123 throw new ArgumentOutOfRangeException("ThreadsMaxIdle", "ThreadsMaxIdle must be greater than or equal to ThreadsMinIdle"); |
|
124 } else if (value < 0) { |
|
125 throw new ArgumentOutOfRangeException("ThreadsMaxIdle", "ThreadsMaxIdle must greater than or equal to 0"); |
|
126 } else { |
|
127 int I = 0; |
|
128 int C = 0; |
|
129 ThreadInfo T = default(ThreadInfo); |
|
130 lock (pIdleThreads) { |
|
131 C = pIdleThreads.Count; |
|
132 if (value < C) { |
|
133 for (I = value; I <= C - 1; I++) { |
|
134 T = pIdleThreads.Dequeue(); |
|
135 T.Abort = true; |
|
136 T.WaitHandle.Set(); |
|
137 } |
|
138 } |
|
139 } |
|
140 pThreadsMaxIdle = value; |
|
141 } |
|
142 } |
|
143 } |
|
144 public int ThreadsMax { |
|
145 get { return pThreadsMax; } |
|
146 set { |
|
147 if (pThreadsMaxIdle > value) { |
|
148 throw new ArgumentOutOfRangeException("ThreadsMax", "ThreadsMax must be greater than or equal to ThreadsMaxIdle"); |
|
149 } else if (value <= 0) { |
|
150 throw new ArgumentOutOfRangeException("ThreadsMax", "ThreadsMax must greater than 0"); |
|
151 } else { |
|
152 pThreadsMax = value; |
|
153 } |
|
154 } |
|
155 } |
|
156 |
|
157 public WorkItem QueueWorkItem(WaitCallback Callback, object State) { |
|
158 WorkItem WorkItem = new WorkItem(); |
|
159 ThreadInfo Thread = null; |
|
160 WorkItem.Callback = Callback; |
|
161 WorkItem.State = State; |
|
162 lock (pIdleThreads) { |
|
163 while (pIdleThreads.Count > 0) { |
|
164 Thread = pIdleThreads.Dequeue(); |
|
165 if (!Thread.Abort) { |
|
166 break; |
|
167 } else { |
|
168 Thread = null; |
|
169 } |
|
170 } |
|
171 } |
|
172 if (Thread == null) { |
|
173 if (pThreads.Count < pThreadsMax) { |
|
174 Thread = StartThread(true); |
|
175 } else { |
|
176 throw new ThreadStateException("Thread limit exceeded"); |
|
177 } |
|
178 } |
|
179 Thread.LastActive = DateTime.Now; |
|
180 WorkItem.Thread = Thread; |
|
181 Thread.WorkItem = WorkItem; |
|
182 Thread.WaitHandle.Set(); |
|
183 return WorkItem; |
|
184 } |
|
185 |
|
186 private ThreadInfo StartThread(bool Reserved) { |
|
187 ThreadInfo Thread = new ThreadInfo(); |
|
188 Thread.Thread = new Thread(pWorker); |
|
189 lock (pThreads) { |
|
190 pThreads.Add(Thread); |
|
191 if (!Reserved) pIdleThreads.Enqueue(Thread); |
|
192 } |
|
193 Thread.LastActive = DateTime.Now; |
|
194 Thread.Thread.Start(Thread); |
|
195 return Thread; |
|
196 } |
|
197 |
|
198 public void AbortAllThreads() { |
|
199 lock (pIdleThreads) { |
|
200 while (pIdleThreads.Count > 0) { |
|
201 ThreadInfo Thread = pIdleThreads.Dequeue(); |
|
202 Thread.Abort = true; |
|
203 Thread.WaitHandle.Set(); |
|
204 } |
|
205 } |
|
206 foreach (ThreadInfo Thread in pThreads.ToArray()) { |
|
207 if (Thread != null && !Thread.Abort) { |
|
208 Thread.Thread.Abort(); |
|
209 Thread.Abort = true; |
|
210 Thread.WaitHandle.Set(); |
|
211 } |
|
212 } |
|
213 pIdleThreads.Clear(); |
|
214 } |
|
215 |
|
216 //ToDo: add timer to kill old threads periodically |
|
217 public void KillOldThreads() { |
|
218 ThreadInfo Thread; |
|
219 lock (pIdleThreads) { |
|
220 if (pIdleThreads.Count == 0) return; |
|
221 Thread = pIdleThreads.Dequeue(); |
|
222 } |
|
223 if (DateTime.Now.Subtract(Thread.LastActive).TotalMinutes > 1) { |
|
224 Thread.Abort = true; |
|
225 Thread.WaitHandle.Set(); |
|
226 } else { |
|
227 lock (pIdleThreads) pIdleThreads.Enqueue(Thread); |
|
228 } |
|
229 } |
|
230 |
|
231 private void pWorker(object state) { |
|
232 ThreadInfo Thread = (ThreadInfo)state; |
|
233 if (Thread == null) throw new ArgumentNullException("state"); |
|
234 try { |
|
235 while (true) { |
|
236 if (Thread.WaitHandle == null) throw new ArgumentNullException("WaitHandle"); |
|
237 if (!Thread.WaitHandle.WaitOne(1000, false)) { |
|
238 if (pBusyThreads.Count == 0) { |
|
239 return; |
|
240 } else { |
|
241 continue; |
|
242 } |
|
243 } |
|
244 if (Thread.Abort) break; |
|
245 |
|
246 Thread.Busy = true; |
|
247 lock (pBusyThreads) pBusyThreads.Add(Thread); |
|
248 try { |
|
249 if (Thread.WorkItem == null) throw new ArgumentNullException("WorkItem"); |
|
250 if (Thread.WorkItem.Callback == null) throw new ArgumentNullException("WorkItem.Callback"); |
|
251 Thread.WorkItem.Callback.Invoke(Thread.WorkItem.State); |
|
252 } catch (ThreadAbortException ex) { |
|
253 ExceptionEventArgs e = new ExceptionEventArgs(Thread.WorkItem, ex, false); |
|
254 if (OnException != null) OnException(this, e); |
|
255 if (e.ThrowError) Console.WriteLine("ThreadAbortException in ThreadPool thread: " + e.Exception.ToString()); |
|
256 return; |
|
257 } catch (Exception ex) { |
|
258 ExceptionEventArgs e = new ExceptionEventArgs(Thread.WorkItem, ex, true); |
|
259 if (OnException != null) OnException(this, e); |
|
260 if (e.ThrowError) { |
|
261 Console.WriteLine("Exception in ThreadPool thread: " + e.Exception.ToString()); |
|
262 throw new Exception("Unhandled exception in work item", e.Exception); |
|
263 } |
|
264 } finally { |
|
265 lock (pBusyThreads) pBusyThreads.Remove(Thread); |
|
266 } |
|
267 Thread.WorkItem.Thread = null; |
|
268 Thread.WorkItem = null; |
|
269 Thread.Busy = false; |
|
270 lock (pIdleThreads) { |
|
271 if (pIdleThreads.Count >= pThreadsMaxIdle) break; |
|
272 pIdleThreads.Enqueue(Thread); |
|
273 } |
|
274 } |
|
275 } finally { |
|
276 Thread.Abort = true; |
|
277 lock (pThreads) pThreads.Remove(Thread); |
|
278 } |
|
279 } |
|
280 } |
|
281 } |