基础观点
1. 历程
历程(Process)是Windows体系中的一个基础观点,它包含着一个运转递次所须要的资本。历程之间是相对自力的,一个历程没法直接接见另一个历程的数据(除非应用分布式盘算体式格局),一个历程运转的失利也不会影响其他历程的运转,Windows体系就是应用历程把事变划分为多个自力的地区的。历程能够理解为一个递次的基础边境。
2. 应用递次域
运用.NET竖立的可实行递次 *.exe,并没有直接承载到历程当中,而是承载到应用递次域(AppDomain)当中。应用递次域是.NET引入的一个新观点,它比历程所占用的资本要少,能够被看做是一个轻量级的历程。
在一个历程中能够包含多个应用递次域,一个应用递次域能够装载一个可实行递次(.exe)或许多个递次集(.dll)。如许能够使应用递次域之间完成深度断绝,纵然历程中的某个应用递次域涌现毛病,也不会影响其他应用递次域的一般运作。
当一个递次集同时被多个应用递次域挪用时,会涌现两种状况:
第一种状况:CLR分别为差别的应用递次域加载此递次集。
第二种状况:CLR把此递次集加载到一切的应用递次域以外,并完成递次集同享,此状况比较特别,被称作为Domain Neutral。
3. 线程
线程(Thread)是历程中的基础实行单位,是递次实行流的最小单位。在历程进口实行的第一个线程被视为这个历程的主线程。在.NET应用递次中,都是以Main()要领作为进口的,当挪用此要领时体系就会自动建立一个主线程。
线程主假如由CPU寄存器、挪用栈和线程当地存储器(Thread Local Storage,TLS)构成的。CPU寄存器重要纪录当前所实行线程的状况,挪用栈重要用于保护线程所挪用到的内存与数据,TLS重要用于寄存线程的状况信息。
4. 三者的关联
历程、应用递次域、线程的关联以下图,一个历程内能够包含多个应用递次域,也有包含多个线程,线程也能够穿越于多个应用递次域当中。但在统一个时刻,线程只会处于一个应用递次域内。
建立要领
1. 下面用火车票体系,引见建立多线程的要领。
起首新建掌握台递次项目,建立火车票类和人类:Ticket和Person.
class Ticket { private int count =100; public int Count { get { return this.count; } } public string GetTicket() { //while (true) //{ this.count++; Thread.Sleep(50); this.count--; //} return "G" + this.count--; } } class Person { private string name, id; private int age; public string Name { get { return this.name; } set { if (value.Length > 0 && value.Length < 8) { this.name = value; } else { throw new IndexOutOfRangeException("Length of name is out of 0~8."); } } } public int Age { get { return this.age; } set { if (value > 0) { this.age = value; } else { throw new IndexOutOfRangeException("Age must be more than 0."); } } } public string ID//身份证 { get { return this.id; } set { if (value.Length == 18) { this.id = value; } else { throw new IndexOutOfRangeException("Lengh of ID must be 16."); } } } public Person(string nameOfPerson, int ageOfPerson, string idOfPerson) { this.name = nameOfPerson; this.age = ageOfPerson; this.id = idOfPerson; } }
其次在Program类中,建立公用的静态要领,背面建立多线程的要领会挪用到。
class Program { static void BuyTicket(object state) { Ticket newTic = (Ticket)state; BuyTicket(newTic); } static string BuyTicket(Ticket newTic) { lock (newTic) { ThreadMessage("Async Thread start:"); Console.WriteLine("Async thread do work!"); string message = newTic.GetTicket(); Console.WriteLine(message + "\n"); return message; } } static void ThreadMessage(string data) { string message = string.Format("{0}\nCurrentThreadId is {1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }
2. 经由过程Thread类建立
它能建立并掌握线程,设置其优先级并猎取其状况。经由过程ThreadStart来建立一个新线程是最直接的要领,这里不做引见。ParameterizedThreadStart托付与ThreadStart托付异常相似,但ParameterizedThreadStart托付是面向带参数要领的。注重ParameterizedThreadStart 对应要领的参数为object,此参数能够为一个值对象,也能够为一个自定义对象。
这里引见经由过程ParameterizedThreadStart建立。
①代码
static void Main(string[] args) { Ticket tic = new Ticket(); Person[] person = new Person[10] { new Person("Nicholas", 21, "000000000000000000"), new Person("Nate", 38, "111111111111111111"), new Person("Vincent", 21, "222222222222222222"), new Person("Niki", 51, "333333333333333333"), new Person("Gary", 28, "444444444444444444"), new Person("Charles", 49, "555555555555555555"), new Person("Karl ", 55, "666666666666666666"), new Person("Katharine", 19, "777777777777777777"), new Person("Lee", 25, "888888888888888888"), new Person("Ann", 34, "99999999999999999"), }; ThreadMessage("MainThread start"); Console.WriteLine(); Thread[] t = new Thread[person.Length]; for (int i = 0; i < person.Length; i++) { t[i] = new Thread(new ParameterizedThreadStart(BuyTicket)); t[i].Start(tic); } for (int i = 0; i < 3; i++) { Console.WriteLine("Main thread do work!"); Thread.Sleep(200); } Console.ReadKey(); }
②运转结果
MainThread startCurrentThreadId is 8Async Thread start: CurrentThreadId is 9Async thread do work! Main thread do work! G100 Async Thread start: CurrentThreadId is 10Async thread do work! G99 Async Thread start: CurrentThreadId is 11Async thread do work! G98 Async Thread start: CurrentThreadId is 12Async thread do work! G97 Async Thread start: CurrentThreadId is 13Async thread do work! Main thread do work! G96 Async Thread start: CurrentThreadId is 14Async thread do work! G95 Async Thread start: CurrentThreadId is 15Async thread do work! G94 Async Thread start: CurrentThreadId is 16Async thread do work! G93 Async Thread start: CurrentThreadId is 17Async thread do work! Main thread do work! G92 Async Thread start: CurrentThreadId is 18Async thread do work! G91
③结论
一共建立了11个线程。
运用ThreadStart与ParameterizedThreadStart竖立新线程异常简朴,但经由过程此要领竖立的线程难于治理,若竖立过量的线程反而会影响体系的机能。当启动一个线程时,会有几百毫秒的时刻消费在预备一些分外的资本上,比方一个新的私有部分变量栈如许的事变。每一个线程会占用(默许状况下)1MB 内存。有见及此,.NET引入CLR线程池这个观点。
3. 运用线程池
线程池(thread pool)能够经由过程同享与接纳线程来减轻这些开支,许可多线程应用在很小的粒度上而没有机能丧失。CLR线程池并不会在CLR初始化的时刻马上竖立线程,而是在应用递次要建立线程来实行使命时,线程池才初始化一个线程。线程的初始化与其他的线程一样。
在完成使命今后,该线程不会自行烧毁,而是以挂起的状况返回到线程池。直到应用递次再次向线程池发出要求时,线程池里挂起的线程就会再度激活实行使命。如许既节省了竖立线程所形成的机能消耗,也能够让多个使命重复重用统一线程,从而在应用递次生存期内勤俭大批开支。
CLR线程池分为事变者线程(workerThreads)与I/O线程 (completionPortThreads) 两种,事变者线程是重要用作治理CLR内部对象的运作,I/O(Input/Output) 线程望文生义是用于与外部体系交流信息。
3.1 经由过程QueueUserWorkItem运用线程池
①代码
static void Main(string[] args) { Ticket tic = new Ticket(); Person[] person = new Person[10] { new Person("Nicholas", 21, "000000000000000000"), new Person("Nate", 38, "111111111111111111"), new Person("Vincent", 21, "222222222222222222"), new Person("Niki", 51, "333333333333333333"), new Person("Gary", 28, "444444444444444444"), new Person("Charles", 49, "555555555555555555"), new Person("Karl ", 55, "666666666666666666"), new Person("Katharine", 19, "777777777777777777"), new Person("Lee", 25, "888888888888888888"), new Person("Ann", 34, "99999999999999999"), }; ThreadPool.SetMaxThreads(1000, 1000); ThreadPool.SetMinThreads(2, 2); ThreadMessage("MainThread start"); Console.WriteLine(); foreach(Person someone in person) { ThreadPool.QueueUserWorkItem(new WaitCallback(BuyTicket), tic); } for (int i = 0; i < 3; i++) { Console.WriteLine("Main thread do work!"); Thread.Sleep(200); } Console.ReadKey(); }
②运转结果
MainThread startCurrentThreadId is 8Main thread do work! Async Thread start: CurrentThreadId is 11Async thread do work! G100 Async Thread start: CurrentThreadId is 10Async thread do work! G99 Async Thread start: CurrentThreadId is 9Async thread do work! G98 Async Thread start: CurrentThreadId is 12Async thread do work! Main thread do work! G97 Async Thread start: CurrentThreadId is 11Async thread do work! G96 Async Thread start: CurrentThreadId is 10Async thread do work! G95 Async Thread start: CurrentThreadId is 9Async thread do work! G94 Async Thread start: CurrentThreadId is 12Async thread do work! Main thread do work! G93 Async Thread start: CurrentThreadId is 11Async thread do work! G92 Async Thread start: CurrentThreadId is 10Async thread do work! G91
③结论
一共建立了5个线程,小于Thread类的10。
经由过程ThreadPool.QueueUserWorkItem启动事变者线程虽然是轻易,但WaitCallback托付指向的必需是一个带有Object参数的无返回值要领,这无疑是一种限定。若要领须要有返回值,或许带有多个参数,这将多费周折。有见及此,.NET供应了另一种体式格局去竖立事变者线程,那就是托付。
3.2 经由过程托付运用线程池
①代码
delegate string MyDelegate(Ticket tic);//能够带多个参数static void Main(string[] args) { Ticket tic = new Ticket(); Person[] person = new Person[10] { new Person("Nicholas", 21, "000000000000000000"), new Person("Nate", 38, "111111111111111111"), new Person("Vincent", 21, "222222222222222222"), new Person("Niki", 51, "333333333333333333"), new Person("Gary", 28, "444444444444444444"), new Person("Charles", 49, "555555555555555555"), new Person("Karl ", 55, "666666666666666666"), new Person("Katharine", 19, "777777777777777777"), new Person("Lee", 25, "888888888888888888"), new Person("Ann", 34, "99999999999999999"), }; ThreadPool.SetMaxThreads(1000, 1000); ThreadPool.SetMinThreads(2, 2); ThreadMessage("MainThread start"); Console.WriteLine(); foreach (Person someone in person) { MyDelegate myDelegate = new MyDelegate(BuyTicket); myDelegate.BeginInvoke(tic, new AsyncCallback(Completed), someone); } for (int i = 0; i < 3; i++) { Console.WriteLine("Main thread do work!"); Thread.Sleep(200); } Console.ReadKey(); }static void Completed(IAsyncResult result) { Console.WriteLine(); ThreadMessage("Async Completed"); //猎取托付对象,挪用EndInvoke要领猎取运转结果 AsyncResult _result = (AsyncResult)result; MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate; string data = myDelegate.EndInvoke(_result); //猎取Person对象 Person person = (Person)result.AsyncState; Console.WriteLine("Person name is " + person.Name); Console.WriteLine("Person age is " + person.Age); Console.WriteLine("Person ID is " + person.ID); Console.WriteLine("Tick ID is "+ data); Console.WriteLine(); }
②运转结果
MainThread start CurrentThreadId is 8Main thread do work!Async Thread start: CurrentThreadId is 10Async thread do work! G100Async Thread start: CurrentThreadId is 9Async thread do work!Async Completed CurrentThreadId is 10Person name is Nicholas Person age is 21Person ID is 000000000000000000Tick ID is G100 G99Async Thread start: CurrentThreadId is 11Async thread do work!Async Completed CurrentThreadId is 9Person name is Nate Person age is 38Person ID is 111111111111111111Tick ID is G99 G98Async Completed CurrentThreadId is 11Person name is Niki Person age is 51Person ID is 333333333333333333Tick ID is G98Async Thread start: CurrentThreadId is 12Async thread do work! Main thread do work! G97Async Thread start: CurrentThreadId is 10Async thread do work!Async Completed CurrentThreadId is 12Person name is Vincent Person age is 21Person ID is 222222222222222222Tick ID is G97 G96Async Thread start: CurrentThreadId is 9Async thread do work!Async Completed CurrentThreadId is 10Person name is Gary Person age is 28Person ID is 444444444444444444Tick ID is G96 G95Async Thread start: CurrentThreadId is 11Async thread do work!Async Completed CurrentThreadId is 9Person name is Charles Person age is 49Person ID is 555555555555555555Tick ID is G95 G94Async Thread start: CurrentThreadId is 12Async Completed CurrentThreadId is 11Person name is Karl Person age is 55Async thread do work! Person ID is 666666666666666666Tick ID is G94 Main thread do work! G93Async Thread start: CurrentThreadId is 10Async thread do work!Async Completed CurrentThreadId is 12Person name is Katharine Person age is 19Person ID is 777777777777777777Tick ID is G93 G92Async Thread start: CurrentThreadId is 9Async thread do work!Async Completed CurrentThreadId is 10Person name is Lee Person age is 25Person ID is 888888888888888888Tick ID is G92 G91Async Completed CurrentThreadId is 9Person name is Ann Person age is 34Person ID is 99999999999999999Tick ID is G91
③结论
一共建立了5个线程。
4. 经由过程TPL建立
从 .NET Framework 4 最先,TPL 是编写多线程代码和并行代码的首选要领。我们先看下MSDN的诠释。
关于多线程,我们常常运用的是Thread。在我们相识Task之前,假如我们要运用多核的功用能够就会本身来开线程,然则这类线程模子在.net 4.0以后被一种称为基于“使命的编程模子”所打击,由于task会比thread具有更小的机能开支,不过人人一定会有迷惑,使命和线程到底有什么区分呢?
使命和线程的区分:
①使命是架构在线程之上的,也就是说使命终究照样要抛给线程去实行。
②使命跟线程不是一对一的关联,比方开10个使命并不是说会开10个线程,这一点使命有点相似线程池,然则使命比拟线程池有很小的开支和准确的掌握。
4.1 经由过程Parallel类建立
①代码
static void Main(string[] args) { Ticket tic = new Ticket(); Person[] person = new Person[10] { new Person("Nicholas", 21, "000000000000000000"), new Person("Nate", 38, "111111111111111111"), new Person("Vincent", 21, "222222222222222222"), new Person("Niki", 51, "333333333333333333"), new Person("Gary", 28, "444444444444444444"), new Person("Charles", 49, "555555555555555555"), new Person("Karl ", 55, "666666666666666666"), new Person("Katharine", 19, "777777777777777777"), new Person("Lee", 25, "888888888888888888"), new Person("Ann", 34, "99999999999999999"), }; Stopwatch watch = new Stopwatch(); ThreadMessage("MainThread start"); Console.WriteLine(); watch.Start(); Parallel.For(0, person.Length, item => { BuyTicket(tic); }); watch.Stop(); Console.WriteLine("Parallel running need " + watch.ElapsedMilliseconds + " ms."); for (int i = 0; i < 3; i++) { Console.WriteLine("Main thread do work!"); Thread.Sleep(200); } Console.ReadKey(); }
②运转结果
MainThread startCurrentThreadId is 9Async Thread start: CurrentThreadId is 9Async thread do work! G100 Async Thread start: CurrentThreadId is 6Async thread do work! G99 Async Thread start: CurrentThreadId is 10Async thread do work! G98 Async Thread start: CurrentThreadId is 11Async thread do work! G97 Async Thread start: CurrentThreadId is 12Async thread do work! G96 Async Thread start: CurrentThreadId is 9Async thread do work! G95 Async Thread start: CurrentThreadId is 6Async thread do work! G94 Async Thread start: CurrentThreadId is 10Async thread do work! G93 Async Thread start: CurrentThreadId is 11Async thread do work! G92 Async Thread start: CurrentThreadId is 12Async thread do work! G91 Parallel running need 534 ms. Main thread do work! Main thread do work! Main thread do work!
③结论
一共建立了5个线程。耗时534ms。
4.2 经由过程Task类建立
建立Task的要领有两种,一种是直接建立——new一个出来,一种是经由过程工场建立。下面引见工场建立要领。
static void Main(string[] args) { Ticket tic = new Ticket(); Person[] person = new Person[10] { new Person("Nicholas", 21, "000000000000000000"), new Person("Nate", 38, "111111111111111111"), new Person("Vincent", 21, "222222222222222222"), new Person("Niki", 51, "333333333333333333"), new Person("Gary", 28, "444444444444444444"), new Person("Charles", 49, "555555555555555555"), new Person("Karl ", 55, "666666666666666666"), new Person("Katharine", 19, "777777777777777777"), new Person("Lee", 25, "888888888888888888"), new Person("Ann", 34, "99999999999999999"), }; Stopwatch watch = new Stopwatch(); ThreadPool.SetMaxThreads(1000, 1000); ThreadPool.SetMinThreads(2, 2); ThreadMessage("MainThread start"); Console.WriteLine(); Dictionary<Person, string> result = new Dictionary<Person, string>(); Task<string>[] tasks = new Task<string>[person.Length]; watch.Start(); for (int i = 0; i < person.Length; i++) { tasks[i] = Task.Factory.StartNew<string>(() => (BuyTicket(tic))); } Task.WaitAll(tasks, 5000);//设置超时5s watch.Stop(); Console.WriteLine("Tasks running need " + watch.ElapsedMilliseconds + " ms." + "\n"); for (int i = 0; i < tasks.Length; i++) { //超时处置惩罚 if (tasks[i].Status != TaskStatus.RanToCompletion) { Console.WriteLine("Task {0} Error!", i + 1); } else { //save result result.Add(person[i], tasks[i].Result); Console.WriteLine("Person name is " + person[i].Name); Console.WriteLine("Person age is " + person[i].Age); Console.WriteLine("Person ID is " + person[i].ID); Console.WriteLine("Tick ID is " + tasks[i].Result); Console.WriteLine(); } } for (int i = 0; i < 3; i++) { Console.WriteLine("Main thread do work!"); Thread.Sleep(200); } Console.ReadKey(); }
②运转结果
MainThread startCurrentThreadId is 10Async Thread start: CurrentThreadId is 6Async thread do work! G100 Async Thread start: CurrentThreadId is 11Async thread do work! G99 Async Thread start: CurrentThreadId is 12Async thread do work! G98 Async Thread start: CurrentThreadId is 13Async thread do work! G97 Async Thread start: CurrentThreadId is 6Async thread do work! G96 Async Thread start: CurrentThreadId is 11Async thread do work! G95 Async Thread start: CurrentThreadId is 12Async thread do work! G94 Async Thread start: CurrentThreadId is 13Async thread do work! G93 Async Thread start: CurrentThreadId is 6Async thread do work! G92 Async Thread start: CurrentThreadId is 11Async thread do work! G91 Tasks running need 528 ms. Person name is Nicholas Person age is 21Person ID is 000000000000000000Tick ID is G100 Person name is Nate Person age is 38Person ID is 111111111111111111Tick ID is G99 Person name is Vincent Person age is 21Person ID is 222222222222222222Tick ID is G97 Person name is Niki Person age is 51Person ID is 333333333333333333Tick ID is G98 Person name is Gary Person age is 28Person ID is 444444444444444444Tick ID is G96 Person name is Charles Person age is 49Person ID is 555555555555555555Tick ID is G95 Person name is Karl Person age is 55Person ID is 666666666666666666Tick ID is G94 Person name is Katharine Person age is 19Person ID is 777777777777777777Tick ID is G93 Person name is Lee Person age is 25Person ID is 888888888888888888Tick ID is G92 Person name is Ann Person age is 34Person ID is 99999999999999999Tick ID is G91 Main thread do work! Main thread do work! Main thread do work!
③结论
一共建立了5个线程。耗时528ms。
Task最吸引人的处所就是他的使命掌握了,你能够很好的掌握task的实行递次,让多个task有序的事变。下面大概说一下:
Task.Wait:就是守候使命实行完成。
Task.WaitAll:就是守候一切的使命都实行完成。
Task.WaitAny:这个用法同Task.WaitAll,就是守候任何一个使命完造诣继承向下实行。
Task.ContinueWith:就是在第一个Task完成后自动启动下一个Task,完成Task的连续。
Task的作废:经由过程cancellation的tokens来作废一个Task。
Task和线程池之间的选择
1. 线程池
这里扼要的剖析下CLR线程池,实在线程池中有一个叫做“全局行列”的观点,每一次我们运用QueueUserWorkItem的运用都邑发生一个“事变项”,然后“事变项”进入“全局行列”举行列队,末了线程池中的的事变线程以FIFO(First Input First Output)的情势掏出,这里值得一提的是在.net 4.0以后“全局行列”采纳了无锁算法,比拟之前版本锁定“全局行列”带来的机能瓶颈有了很大的改变。
那末使命托付的线程池不光有“全局行列”,而且每一个事变线程都有”部分行列“。我们的第一回响反映一定就是“部分行列“有什么优点呢?这里暂且不说,我们先来看一下线程池中的使命分派,以下图:
线程池的事变体式格局大抵以下,线程池的最小线程数是6,线程1~3正在实行使命1~3,当有新的使命时,就会向线程池要求新的线程,线程池会将余暇线程分派出去,当线程不足时,线程池就会建立新的线程来实行使命,直到线程池到达最大线程数(线程池满)。总的来说,只要有使命就会分派一个线程去实行,当FIFO非常频仍时,会形成很大的线程治理开支。
2. Task
当我们new一个task的时刻“事变项”就会进去”全局行列”,假如我们的task实行的异常快,那末“全局行列“就会FIFO的异常频仍,那末有什么方法减缓呢?
当我们的task在嵌套(见附录)的场景下,“部分行列”就要发生结果了,比方我们一个task内里有3个task,那末这3个task就会存在于“部分行列”中,以下图的使命一,内里有三个使命要实行,也就是发生了所谓的”部分行列”,当使命三的线程实行完成时,就会从使命一种的行列中以FIFO的情势”盗取”使命实行,从而减少了线程治理的开支。
这就相当于,有两个人,一个人干完了分派给本身的一切活,而另一个人却另有许多的活,闲的人应当接办点忙的人的活,一同疾速完成。
从上面各种状况我们看到,这些分流和负载都是一般ThreadPool.QueueUserWorkItem所不能办到的,所以说在.net 4.0以后,我们尽量的运用TPL,扬弃ThreadPool。
附录 Task的嵌套
1. 非关联嵌套
Task中的嵌套分为两种,关联嵌套和非关联嵌套,就是说内层的Task和外层的Task是不是有联络,下面我们编写代码先来看一下非关联嵌套,及内层Task和外层Task没有任何关联,照样在掌握台递次下面,代码以下:
static void Main(string[] args) { var pTask = Task.Factory.StartNew(() => { var cTask = Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(2000); Console.WriteLine("Childen task finished!"); }); Console.WriteLine("Parent task finished!"); }); pTask.Wait(); Console.WriteLine("Flag"); Console.Read(); }
运转后,输出以下信息:
从图中我们能够看到,外层的pTask运转完后,并不会守候内层的cTask,直接向下走先输出了Flag。这类嵌套有时刻相当于我们建立两个Task,然则嵌套在一同的话,在Task比较多时会轻易查找和治理,而且还能够在一个Task半途到场多个Task,让进度并行行进。
2. 关联嵌套
下面我们来看一下怎样建立关联嵌套,就是建立有父子关联的Task,修正上面代码以下:
static void Main(string[] args) { var pTask = Task.Factory.StartNew(() => { var cTask = Task.Factory.StartNew(() => { System.Threading.Thread.Sleep(2000); Console.WriteLine("Childen task finished!"); },TaskCreationOptions.AttachedToParent); Console.WriteLine("Parent task finished!"); }); pTask.Wait(); Console.WriteLine("Flag"); Console.Read(); }
能够看到,我们在建立cTask时,到场了以参数,TaskCreationOptions.AttachedToParent,这个时刻,cTask和pTask就会竖立关联,cTask就会成为pTask的一部分,运转代码,看下结果:
能够看到,tTask会守候cTask实行完成。免得我们写Task.WaitAll了,外层的Task会自动守候一切的子Task完成才向下走。
3. 综合
下面我们来写一个Task综合运用的例子,来看一下多使命是怎样合作的。假设有以下使命,如图:
使命2和使命3要守候使命1完成后,获得使命1的结果,然后最先实行。使命4要守候使命2完成,获得其结果才实行,终究使命3和使命4都完成了,兼并结果,使命完成。图中已说的很邃晓了。下面来看一下代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TaskDemo { class Program { static void Main(string[] args) { Task.Factory.StartNew(() => { var t1 = Task.Factory.StartNew<int>(() => { Console.WriteLine("Task 1 running..."); return 1; }); t1.Wait(); //守候使命一完成 var t3 = Task.Factory.StartNew<int>(() => { Console.WriteLine("Task 3 running..."); return t1.Result + 3; }); var t4 = Task.Factory.StartNew<int>(() => { Console.WriteLine("Task 2 running..."); return t1.Result + 2; }).ContinueWith<int>(task => { Console.WriteLine("Task 4 running..."); return task.Result + 4; }); Task.WaitAll(t3, t4); //守候使命三和使命四完成 var result = Task.Factory.StartNew(() => { Console.WriteLine("Task Finished! The result is {0}",t3.Result + t4.Result); }); }); Console.Read(); } } }
使命2和使命4能够用ContinueWith衔接实行,终究运转结果如图:
能够看到一切的使命都实行了,我们也得到了准确的结果11.
以上就是从0自学C#11--多线程建立要领汇总以及优缺点的内容,更多相关内容请关注ki4网(www.ki4.cn)!