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