对于所有需要等待 的操作,例 如 ,因 为文件 、 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的。
2 Parallel类
2.1 用Parallel.For()方法循环
Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代。迭代的顺序没有定义。
1 ParallelLoopResult result = Parallel.For(0, 10, i => 2 { 3 Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); 4 Thread.Sleep(10); 5 }); 6 Console.WriteLine(result.IsCompleted);
在For()方法中,前两个参数定义了循环的开头和结束。从输出可以看出,顺序是不能保证的。也可以提前中断Parallel.For()方法。
1 ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) => 2 { 3 Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId); 4 Thread.Sleep(10); 5 if (i > 15) 6 pls.Break(); 7 }); 8 Console.WriteLine(result2.IsCompleted); 9 Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
2.2 用Parallel.ForEach()方法循环
paraller.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于Foreach语句,但以异步方式遍历,这里也没有确定的遍历顺序。
1 string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" }; 2 // Parallel.ForEach(data, s => { Console.WriteLine(s); }); 3 Parallel.ForEach(data, (s, pls) => { if (s == "one") { Console.WriteLine("break......"); pls.Break(); } Console.WriteLine(s); Thread.Sleep(100); });
2.3 通过Paraller.Invoke()调用多个方法
Parallel.Invoke(Foo ,Bar); static void Foo() { Console.WriteLine("foo"); } static void Bar() { Console.WriteLine("bar"); }
3 任务
.NET 4 包含新的名称空间System.Threading.Task,它它 包含的类抽象出了线程功能,在后台使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。
3.1启动任务
要启动任务,可 以使用 TaskFactory类 或 Task类 的构造函数和 start()方 法。 Task类 的构造函数在创建任务上提供的灵活性较大.
1 //using TaskFactory 2 Task t1 = new TaskFactory().StartNew(TaskMethod); 3 //using the task factory via task 4 Task t2 = Task.Factory.StartNew(TaskMethod); 5 //using task constructor 6 Task t3 = new Task(TaskMethod); 7 t3.Start();
使用 Task类 的构造函数和 TaskFactory类 的 stamw()方法时,都可以传递TaskCreationOptions枚举中的值。 设置LongRunning选项,可 以通知任务调度器,该 任务需要较长时间执行,这样调度器更可能使用 新线。 如果该任务应关联到父任务上,而父任务取消了,则 该任务也应取消,此 时应设置 AuachToParent选 项。PerferFairness 值表示,调度器应提取出已在等待的第一个任务。 如果任务使用 子任务创建了其他工作,子
任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应 以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness
1 Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
3.2连续的任务
通过任务,可 以指定在任务完成后,应 开始运行另一个特定任务.
1 static void DoOnFirst() 2 { 3 Console.WriteLine("doing some task {0}",Task.CurrentId); 4 Thread.Sleep(3000); 5 } 6 static void DoSecond(Task t) 7 { 8 Console.WriteLine("task {0} finished",t.Id); 9 Console.WriteLine("this task id {0}",Task.CurrentId); 10 Console.WriteLine("do some cleanup"); 11 Thread.Sleep(3000); 12 } 13 14 Task t1 = new Task(DoOnFirst); 15 Task t2 = t1.ContinueWith(DoSecond); 16 Task t3 = t2.ContinueWith(DoSecond); 17 Task t4 = t3.ContinueWith(DoSecond); 18 Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness); 19 t1.Start();
无论前一个任务是如何结束的,前 面 的连续任务总是在前一个任务结束时启 动 。 使用TaskContinuationOptions 枚举中的值,可 以指定,连续任务只有在起始任务成功(或失败)结束时启动。
3.3任务层次的结构
1 static void ParentAndChild() 2 { 3 var parent = new Task(ParentTask); 4 parent.Start(); 5 Thread.Sleep(2000); 6 Console.WriteLine(parent.Status); 7 Thread.Sleep(4000); 8 Console.WriteLine(parent.Status); 9 Console.WriteLine(); 10 } 11 private static void ParentTask() 12 { 13 Console.WriteLine("task id {0}",Task.CurrentId); 14 var child = new Task(ChildTask); 15 child.Start(); 16 Thread.Sleep(1000); 17 Console.WriteLine("parent started child"); 18 } 19 20 private static void ChildTask() 21 { 22 Console.WriteLine("child"); 23 Thread.Sleep(5000); 24 Console.WriteLine("child finished"); 25 }
如果父任务在子任务之前结束 ,父 任务的状态就显示为WaitingForChildrenToComplete.只要子任务也结束 时,父任务的状态就变成RanToCompletion。 ·
4 取消架构
4.1Parallel.For()方法的取消
1 var cts = new CancellationTokenSource(); 2 cts.Token.Register(() => Console.WriteLine("token canceled")); 3 new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start(); 4 try 5 { 6 ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x => 7 { 8 Console.WriteLine("loop {0} started", x); 9 int sun = 0; 10 for (int i = 0; i < 100; i++) 11 { 12 Thread.Sleep(2); 13 sun += i; 14 } 15 Console.WriteLine("loop {0} finished",x); 16 }); 17 } 18 catch (Exception ex) 19 { 20 Console.WriteLine(ex.Message); 21 }
4.2任务的取消
同样的取消模式也可用于任务。
5 线程池
如果有不同的小任务要完成,就可以事先创建许多线程 ,· 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在 需要释放资源时减少。不需要自己创建这样一个列表。 该列表由 ThreadPool类 托管。 这个类会在需要时增减池中线程的线程数,直 到最大的线程数。 池中的最大线程数是可配置的。如果有更多的作业要处理,线 程池中线程的个数也到了极限,最 新的作业就要排队,且 必须等待线程完成其任务。
1 static void Main(string[] args) 2 { 3 int nWorkerThreads; 4 int nCompletionPortThreads; 5 ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads); 6 Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads); 7 8 for (int i = 0; i < 5; i++) 9 { 10 ThreadPool.QueueUserWorkItem(JobForAThread); 11 } 12 Thread.Sleep(3000); 13 Console.ReadKey(); 14 } 15 static void JobForAThread(object obj) 16 { 17 for (int i = 0; i < 3; i++) 18 { 19 Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId); 20 Thread.Sleep(30); 21 } 22 }