CLR线程池
虽然CLR通过System.Threading名称空间提供了对Windows线程API的封装,但直接创建和销毁线程严重影响性能。过多的线程不仅浪费内存,而且会造成频繁的CPU上下文切换,更重要的是线程大部分时间都处于无所事事的状态,浪费宝贵的CPU时间。其次多线程非常容易引发滥用,特别是那些没什么多线程基础的程序员,为了所谓的“性能”无脑使用线程,即使在一些商业软件中这种现象也比比皆是。为了减少这种现象,CLR提供了原生的线程池支持。对于每个CLR,线程池线程将被所有运行中的托管程序集共享。
线程池调度
CLR的初始线程池为空,每当托管程序请求使用线程池线程或执行异步任务,CLR会将程序请求的任务添加到线程池的任务记录,并分配给一个线程执行。如果没有可用线程,则创建一个新线程。与直接使用Thread相比,线程池线程完成任务后并不会销毁,而是进入线程池等待新任务。CLR线程池使用启发式的线程分配策略,当有大量任务请求时会创建较多的线程,而空闲时则销毁多余的线程以节省内存。
执行上下文
CLR中每个线程都与一个执行上下文相关联,其中包括运行权限、用户标识以及区域设置等等信息,每当线程被创建时,会从被创建的线程复制一份执行上下文。对于不需要执行上下文信息的线程来说,这一操作明显影响性能。因此CLR提供了ExecutionContext类来对执行上下文进行控制。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main() { CallContext.LogicalSetData("User","Admin"); ThreadingPool.QueueUserWorkItem((state)=>Console.WriteLine(CallContext.LogicalGetData("User").ToString())); ExecutionContext.SuppressFlow(); ThreadingPool.QueueUserWorkItem((state)=>Console.WriteLine(CallContext.LogicalGetData("User").ToString())); ExecutionContext.RestoreFlow(); }
|
取消与超时
线程池比Thread类更好的一点是支持协作式取消以及任务超时,只需要将一个CancellatinTokenSource对象与任务相关联,即可方便的取消任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public static void main() { CancellationTokenSource cts = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(state=>Count(cts.Token,1000000)); Console.WriteLine("回车键取消..."); Console.ReadLine(); cts.Cancel(); }
private void Count(CancellationToken token, Int32 count) { for(int i=0;i<=count;i++) { if(token.IsCancellationRequested) { Console.WriteLine("已取消"); break; } Console.WriteLine(i); Thread.Sleep(200); } }
|
CancellationTokenSource类
以下是CTS的.NET源代码节选:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public class CancellationTokenSource: IDisposable { private volatile ManualResetEvent m_kernelEvent; private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists; private const int CANNOT_BE_CANCELED = 0; private const int NOT_CANCELED = 1; private const int NOTIFYING = 2; private const int NOTIFYINGCOMPLETE = 3; private volatile int m_state; public CancellationToken token{get}; public bool IsCancellationRequested { get{return m_state>=NOTIFING;} }
internal WaitHandle WaitHandle { if (m_kernelEvent != null) return m_kernelEvent; ManualResetEvent mre = new ManualResetEvent(false); if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null) { ((IDisposable)mre).Dispose(); } if (IsCancellationRequested) m_kernelEvent.Set(); return m_kernelEvent; }
public Cancel(bool throwOnFirstException) { ThrowIfDisposed(); NotifyCancellation(throwOnFirstException); }
private void NotifyCancellation(bool throwOnFirstException) { if (IsCancellationRequested) return;
if (Interlocked.CompareExchange(ref m_state, NOTIFYING, NOT_CANCELED) == NOT_CANCELED) { Timer timer = m_timer; if(timer != null) timer.Dispose(); if (m_kernelEvent != null) m_kernelEvent.Set(); ExecuteCallbackHandlers(throwOnFirstException); } } public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) { }
private CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext, bool useExecutionContext) { } }
public struct CancellationToken { private CancellationTokenSource m_source; public bool IsCancellationRequested {get public WaitHandle WaitHandle {get public CancellationTokenRegistration Register(Action callback, bool useSyncContext) }
|