线程基础和异步执行计算限制
线程基础
进程是一个应用程序实例要使用资源的集合。无法与windows内核的数据和代码沟通,维持系统的健壮性。
线程是一个虚拟的cpu,防止和其他的程序混在一起,最终是操作系统瘫痪掉。
线程的要素:线程内核对象、线程环境块、用户模式栈、内核模式栈、Dll线程链接和线程分离通知。
A、线程内核对象:每个线程初始化时都带有这种数据结构。数据结构包括描述线程的属性和线程上下文(上下文是内存块,主要包含CPU的寄存器集合,寄存器是cpu的组成部分,可以用来暂存指令,数据和地址。)
B、线程环境块:是在用户模式中分配和初始化的内存块(内存是是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据)
C、用户模式栈:主要存储传给方法的变量和实参。还包括一个地址,知道方法执行完从哪里继续。
D、内核模式栈:如果程序需要和操作系统内核打交道的话,需要把用户模式栈中的数据传到内核模式栈,经过验证后,让os进行相关操作。
E、dll线程链接和线程分离通知,一种标志。
时间片的切换让操作系统可以一个程序进入了死循环,然后可以继续执行其他程序,而不需要重启操作系统,但切换影响效率。
clr使用的是windows的线程处理能力。
Console.WriteLine("Main thread: starting a dedicated thread " + "to do an asynchronous operation"); Thread dedicatedThread = new Thread(ComputeBoundOp); dedicatedThread.Start(5); Console.WriteLine("Main thread: Doing other work here..."); //Thread.Sleep(10000); // Simulating other work (10 seconds) dedicatedThread.Join(); // 等待dedicatedThread线程执行完才会继续下面的线程 Console.WriteLine("haiziguo"); Console.ReadLine(); } // This method's signature must match the ParametizedThreadStart delegate private static void ComputeBoundOp(Object state) { // This method is executed by a thread pool thread Console.WriteLine("In ComputeBoundOp: state={0}", state); Thread.Sleep(10000); // Simulates other work (1 second) // When this method returns, the dedicated thread dies }
所有的前台线程停止,后台线程必须停止,且不会抛出异常。
计算限制异步编程——线程池
CLR包含了代码来管理他自己的线程池。一个CLR包括一个线程池。
默认线程池创建的线程都是后台线程。
线程池将自己的线程划分为工作者线程和IO线程。
IO线程用于通知你的代码一个异步IO限制操作已经完成。
异步编程模式发出IO请求,比如访问文件、网络服务器、数据库、web服务、其他硬件设备。
线程上下文(CallContext)通常是在主线程流向辅助线程,以使主线程和辅助线程的各种权限一致,当然可以阻止流动。
CallContext.LogicalSetData("Name", "Jeffrey"); // Initiate some work to be done by a thread pool thread // The thread pool thread can access the logical call context data ThreadPool.QueueUserWorkItem( state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name"))); // Suppress the flowing of the Main thread’s execution context ExecutionContext.SuppressFlow(); // Initiate some work to be done by a thread pool thread // The thread pool thread can NOT access the logical call context data ThreadPool.QueueUserWorkItem( state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));
取消线程操作:
private static void CancellingAWorkItem() { CancellationTokenSource cts = new CancellationTokenSource(); // Pass the CancellationToken and the number-to-count-to into the operation ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000)); Console.WriteLine("Press <Enter> to cancel the operation."); Console.ReadLine(); cts.Cancel(); // If Count returned already, Cancel has no effect on it // Cancel returns immediately, and the method continues running here... Console.ReadLine(); // For testing purposes } private static void Count(CancellationToken token, Int32 countTo) { for (Int32 count = 0; count < countTo; count++) { if (token.IsCancellationRequested) { Console.WriteLine("Count is cancelled"); break; // Exit the loop to stop the operation } Console.WriteLine(count); Thread.Sleep(200); // For demo, waste some time } Console.WriteLine("Count is done"); }
如果禁止被取消时,可以传入一个Token的静态变量。可以使用CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);来创建多个,一个取消时,LinkedTokenSource就取消。可以创建多个。
计算限制异步编程——任务
线程池的QueueUserWorkItem没有返回值,不知道什么时间结束。下面给出含有返回值的Task代码
Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10000); // You can start the task sometime later t.Start(); // Optionally, you can explicitly wait for the task to complete t.Wait(); // FYI: Overloads exist accepting a timeout/CancellationToken // Get the result (the Result property internally calls Wait) Console.WriteLine("The sum is: " + t.Result); // An Int32 value
Task条用Wait的两种情况:a.已经开始Start,调用Task的线程阻塞,直到Task完成为止;b.没有start,调用Task线程不会出现阻塞,而是使用需要等Task完成后才继续线程原来正在执行的操作。可以理解为一个是“线程阻塞”,一个是“步骤阻塞”。
当Task出现异常的话,只有在使用wait和result时才能发现。
下面代码演示如何判断取消Task
private static Int32 Sum(CancellationToken ct, Int32 n) { Int32 sum = 0; for (; n > 0; n--) { // The following line throws OperationCanceledException when Cancel // is called on the CancellationTokenSource referred to by the token //如果使用CancellationToken的取消,那么就抛出异常, //判断是否是指定的异常,来判断是否是取消了任务 ct.ThrowIfCancellationRequested(); Console.WriteLine(n); //Thread.Sleep(0); // Simulate taking a long time checked { sum += n; } } return sum; }
private static void Cancel() { CancellationTokenSource cts = new CancellationTokenSource(); Task<Int32> t = new Task<Int32>(() => Sum(cts.Token, 10000), cts.Token); t.Start(); // Sometime later, cancel the CancellationTokenSource to cancel the Task cts.Cancel(); try { // If the task got canceled, Result will throw an AggregateException Console.WriteLine("The sum is: " + t.Result); // An Int32 value } catch (AggregateException ae) { // Consider any OperationCanceledException objects as handled. // Any other exceptions cause a new AggregateException containing // only the unhandled exceptions to be thrown ae.Handle(e => e is OperationCanceledException); // If all the exceptions were handled, the following executes Console.WriteLine("Sum was canceled"); } }
一个任务完成后继续另外一个任务,继续的任务会自动开始
//继续的任务会自动开始 // Create Task, defer starting it, continue with another task Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10000); // You can start the task sometime later t.Start(); // ContinueWith returns a Task but you usually don't care Task cwt = t.ContinueWith(task => Console.WriteLine("The sum is: " + task.Result)); //cwt.Start();
执行定时计算限制操作
// // 摘要: // 用 32 位无符号整数来度量时间间隔,以初始化 Timer 类的新实例。 // // 参数: // callback: // 一个 System.Threading.TimerCallback 委托,表示要执行的方法。 // // state: // 一个包含回调方法要使用的信息的对象,或者为 null。 // // dueTime: // 调用 callback 之前延迟的时间量(以毫秒为单位)。 指定 System.Threading.Timeout.Infinite 可防止启动计时器。 // 指定零 (0) 可立即启动计时器。 // // period: // 调用 callback 的时间间隔(以毫秒为单位)。 指定 System.Threading.Timeout.Infinite 可以禁用定期终止。 // // 异常: // System.ArgumentOutOfRangeException: // dueTime 或 period 参数为负,并且不等于 System.Threading.Timeout.Infinite。 // // System.ArgumentNullException: // callback 参数为 null。 [CLSCompliant(false)] [SecuritySafeCritical] public Timer(TimerCallback callback, object state, uint dueTime, uint period);
线程池为所有的Timer对象只使用一个线程。当第一个对象还没有执行完,下一个就开始了,肯定会出现重复执行timer的事件了,为了防止这样事件的放生,可以如下
Timer timer = new Timer((s) => { for (int i = 0; i < 1000; i++) { Console.WriteLine(i); } }, null, 0,Timeout.Infinite); Thread.Sleep(5000); timer.Change(0, Timeout.Infinite);
Timeout.Infinite, 然后再使用Change。