对于全部须要等待 的操做,例 如 ,因 为文件 、 数据库或网络访 问都须要必定 的时间,此 时就能够启 动一个新线程,同时完成其余任务,即便是处理密集型的任务,线程也是有帮助的。数据库
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);
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); });
Parallel.Invoke(Foo ,Bar); static void Foo() { Console.WriteLine("foo"); } static void Bar() { Console.WriteLine("bar"); }
.NET 4 包含新的名称空间System.Threading.Task,它它 包含的类抽象出了线程功能,在后台使用ThreadPool。 任务表示应完成的某个单元的工做。 这个单元的工做能够在单独的线程中运行,也能够以同步方式启动一个任务,这须要等待主调线程。架构
要启动任务,可 以使用 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 值表示,调度器应提取出已在等待的第一个任务。 若是任务使用 子任务建立了其余工做,子
任务就优先于其余任务。 它们不会排在线程池队列中的最后。 若是这些任务应 以公平的方式与全部其余任务一块儿处理,就设置该选项为PreferFairnesside
1 Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
经过任务,可 以指定在任务完成后,应 开始运行另外一个特定任务.函数
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 枚举中的值,可 以指定,连续任务只有在起始任务成功(或失败)结束时启动。oop
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。 ·this
4 取消架构
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 }
一样的取消模式也可用于任务。
若是有不一样的小任务要完成,就能够事先建立许多线程 ,· 在应完成这些任务时发出请求。 这个线程数最好在须要更多的线程时增长,在 须要释放资源时减小。不须要本身建立这样一个列表。 该列表由 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 }
线程池使用起来很简单,但 它有一些限制 :
使用Thread类能够建立和控制线程,
1 new Thread(() => { Console.WriteLine("Running in thread"); }).Start(); 2 Console.WriteLine("this is the main thread");
给线程传递一些数据能够采用2中方式,一种是使用带ParameterizdThreadStart委托参数的Thread构造函数,另外一种方式是常见一个自定义的类,把线程的方法定义为实例方法。
只要有一个前台相称在运行,程序的进程就在运行,若是前台多个线程在运行,而Main()方法结束了,应用程序的进程直到全部前台完成其任务前都处于激活状态。默认状况下,用Thread建立的线程为前台线程,线程池中的线程为老是为后台线程。Thread类能够设置IsBackground属性设置是否为前台线程。
1 static void Main(string[] args) 2 { 3 var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false }; 4 t1.Start(); 5 Console.WriteLine("Main thread ending now"); 6 Console.ReadKey(); 7 } 8 9 10 static void ThreadMain() 11 { 12 Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name); 13 Thread.Sleep(5000); 14 Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name); 15 }
线 程曲操做系统调度。 给线程指定优先级,就 可 以影响调度顺序。在Thread类中,能够设置Priority属性设置线程的优先级,Priority属性须要ThreadPriority枚举定义的一个值,定义级别有Highest,AboveNormal,Normal,BelowNormal和Lowest。
调用 Thread对 象的Start()方 法,可 以建立线程。 可是,在 调用Strat()方法后,新线程仍不是处于 Running状态,而 是处于 Unstarted状 态。 只要操做系统的线程调度器选择了要运行的线程,线程就会改成Running状态 。 读取Thread.ThreadState属 性,就能够得到线程的当前状态。使用 Thread.Sleep() 方法 ,会使线程处于WaitSleepJoin状态,在 经历Sleep()方法定义的时间段后 ,线程就会等待再次被唤醒。要中止另外一个线程,可 以调用Thread.Abort()方 法。 调用这个方法时,会 在接到终止命令的线程中抛出一个ThreadAbortException类 型的异常。 用一个处理程序捕获这个异常,线程可 以在结束前完成一些清理工做。如 果须要等待线程的结束,就 可 以调用Thread.Join()方 法 。此方 法会中止当前线程 ,并把它设置为WaitSleepJoin状 态 ,直 到加入 的线程完成为止 。
若是两个或多个线程访问相同的对象,或 者访问不一样步的共享状态,就会出现争用条件。
过多的锁定也会有麻烦。 在死锁中,至少有两个线程被挂起,并等待对方解除锁定。 因为两个线程都在等待对方,就 出现了死锁,线程将无限等待下去。
C#为多个线程的同步提供了 本身的关键字:lock语 句 。 lock语 句是设置锁定和解除锁定的一种简单方式。
1 static void Main() 2 { 3 int numTask = 20; 4 var state = new ShareState(); 5 var tasks = new Task[numTask]; 6 for (int i = 0; i < numTask; i++) 7 { 8 tasks[i] = new Task(new Job(state).DoWork); 9 tasks[i].Start(); 10 } 11 12 for (int i = 0; i < numTask; i++) 13 { 14 tasks[i].Wait(); 15 } 16 Console.WriteLine("Sun :{0}",state.State); 17 Console.ReadKey(); 18 } 19 } 20 public class Job 21 { 22 ShareState shareState; 23 public Job(ShareState shareState) 24 { 25 this.shareState = shareState; 26 } 27 public void DoWork() 28 { 29 for (int i = 0; i < 5000; i++) 30 { 31 shareState.State += 1; 32 } 33 } 34 } 35 public class ShareState 36 { 37 public int State { get; set; } 38 }
上面的代码,由于执行了5000次循环,有20个任务,因此输出的值应为100000,可是,事实并不是如此。使用Lock修改DoWork方法
1 public void DoWork() 2 { 3 for (int i = 0; i < 5000; i++) 4 { 5 lock (shareState) 6 shareState.State += 1; 7 } 8 }
这样结果老是正确的。可是在一个地方使用Lock语句并不意味着,访问对象的其余线程都在等待,必须对每一个访问共享状态的线程显示的使用同步功能。继续需改
1 static void Main() 2 { 3 int numTask = 20; 4 var state = new ShareState(); 5 var tasks = new Task[numTask]; 6 for (int i = 0; i < numTask; i++) 7 { 8 tasks[i] = new Task(new Job(state).DoWork); 9 tasks[i].Start(); 10 } 11 12 for (int i = 0; i < numTask; i++) 13 { 14 tasks[i].Wait(); 15 } 16 Console.WriteLine("Sun :{0}", state.State); 17 Console.ReadKey(); 18 } 19 } 20 public class Job 21 { 22 ShareState shareState; 23 public Job(ShareState shareState) 24 { 25 this.shareState = shareState; 26 } 27 public void DoWork() 28 { 29 for (int i = 0; i < 5000; i++) 30 { 31 shareState.IncrementState(); 32 } 33 } 34 } 35 public class ShareState 36 { 37 private int state = 0; 38 private object obj = new object(); 39 public int State 40 { 41 get 42 { 43 return state; 44 } 45 } 46 public int IncrementState() 47 { 48 lock(obj) 49 return ++state; 50 } 51 }
Ihterlockcd类用 于使变量的简单语旬原子化。 i++不是线程安全的,它 的操做包括从内存中获取一个值,给该值递增 1,再 将它存储回内存。 这些操做均可能会被线程调度器打断。 Ihterlocked类提供了以线程安全的方式递增、 递减、'交换和读取值的方法。 与其余同步技术相 比,使用 Ihterlocked类 会快得多。 可是,它 只能用于简单的同步问题。
C#的lock语 句 ,由编译器解析为使用monitor类,与C#的 lock语 句相 比,Monitor 类的主要优势是:可 以添加一个等待被锁定的超时值 。 这样就不会无限期地等待被锁定.
1 object obj = new object(); 2 bool lockTaken = false; 3 Monitor.TryEnter(obj, 500, ref lockTaken); 4 if (lockTaken) 5 { 6 try 7 { 8 //已经锁定,想干吗就干吗吧 9 } 10 finally 11 { 12 Monitor.Exit(obj); 13 } 14 } 15 else 16 { 17 //没有锁定,当心喽 18 }
Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多个进程同步访问的一个类 因为系统能识别有名称的互斥,因 此可 以使用 它禁止应用程序启动两次
1 static class Program 2 { 3 /// <summary> 4 /// 应用程序的主入口点。 5 /// </summary> 6 [STAThread] 7 static void Main() 8 { 9 bool createNew; 10 11 Mutex m = new Mutex(false, "test", out createNew); 12 if (!createNew) 13 { 14 MessageBox.Show("程序已启动"); 15 Application.Exit(); 16 return; 17 } 18 Application.EnableVisualStyles(); 19 Application.SetCompatibleTextRenderingDefault(false); 20 Application.Run(new Form1()); 21 } 22 }