[转]C#综合揭秘——细说多线程(上)

引言

正文重要由线程的根基由此法,CLR线程池当中工作者线程与I/O线程的支出,并行操作PLINQ等多独面介绍多线程的付出。
其中托的BeginInvoke方法以及回调函数最为常用。 而
I/O线程可能容易遭遇大家之大意,其实当支付多线程系统,更该多留意I/O线程的操作。特别是在ASP.NET开发中,可能更六个人唯有会面小心在客户端接纳Ajax或者以劳务器端使用UpdatePanel。其实合情合理使用I/O线程在报道类要文件下充斥时,能尽量地压缩IIS的下压力。
并行编程是Framework4.0遇奋力推广的异步操作办法,更值得更透彻地修。
希望本篇小说会针对各位的学习钻探有助,当中有错漏的地点约点评。

 

 

目录

无异于、线程的概念

第二、线程的基础知识

老三、以ThreadStart模式实现多线程

季、CLR线程池的劳重力线程

五、CLR线程池的I/O线程

六、异步
SqlCommand

七、并行编程与PLINQ

八、计时器与锁

 

 

 

 

一如既往、线程的概念

 1. 1 经过、应用程序域与线程的关联

经过(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所要的资源。进程中是绝对独立的,一个历程无法访问另一个历程的数额(除非选拔分布式统计模式),一个进程运行的黄也非碰面影响其他进程的运作,Windows系统就是下过程将工作分为多单独立的区域之。进程可以领悟也一个序的主导边界。

应用程序域(AppDomain)是一个程序运行的逻辑区域,它好说是一个轻量级的经过,.NET的程序集正是以应用程序域中运行的,一个经过可以分包有差不六个应用程序域,一个应用程序域也足以涵盖多单次集。在一个应用程序域中含了一个依旧多独上下文context,使用前后文CLR就可知管某些特殊对象的状态放置在不同容器中。

线程(Thread)是过程被的骨干举办单元,在经过入口实施的率先独线程被视为是历程的主线程。在.NET应用程序中,都是为Main()方法作为入口的,当调用此办法时系统即会自行创立一个主线程。线程重尽管由于CPU寄存器、调用栈和线程本地存储器(Thread
Local
Storage,TLS)组成的。CPU寄存器首要记录时所行线程的状态,调用栈重要用于保障线程所调用到之内存和数据,TLS首要用来存放线程的状态音信。

过程、应用程序域、线程的关联要下图,一个进程内可以包多独应用程序域,也发包括四个线程,线程也堪不断于多单应用程序域当中。但以同一个每日,线程只会合处于一个应用程序域内。

 

图片 1

 
由于本文是以介绍多线程技术为主题,对经过、应用程序域的牵线就到这一个截止。关于进程、线程、应用程序域的技能,在“C#汇总揭秘——细说进程、应用程序域与上下文”会发生详实介绍。

 

1.2 多线程

在单CPU系统的一个单位时(time
slice)内,CPU只好运行单个线程,运行顺序取决于线程的预级别。假诺当单位时外线程未能形成实施,系统就会管线程的状态信息保存到线程的地点存储器(TLS)
中,以便下次执行时回升执行。而多线程只是网带来的一个借像,它当差不两个单位时外开展多单线程的切换。因为切换频密而且单位时很短,所以多线程可吃作为同时运转。

确切使用多线程能增进系统的性能,比如:在网要大容量的数平常以多线程,把数量输出工作付出异步线程,使主线程保持其安居去处理任何问题。但用留意一点,因为CPU需要花不少之日子在线程的切换上,所以重重地以多线程反而会招致性的跌。

 

回到目录

亚、线程的基础知识

2.1 System.Threading.Thread类

System.Threading.Thread是用以控制线程的功底类,通过Thread可以决定时应用程序域中线程的创立、挂于、截至、销毁。

她概括以下常用公共性质:

属性名称 说明
CurrentContext 获取线程正在其中执行的当前上下文。
CurrentThread 获取当前正在运行的线程。
ExecutionContext 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive 获取一个值,该值指示当前线程的执行状态。
IsBackground 获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread 获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId 获取当前托管线程的唯一标识符。
Name 获取或设置线程的名称。
Priority 获取或设置一个值,该值指示线程的调度优先级。
ThreadState 获取一个值,该值包含当前线程的状态。

 

2.1.1 线程的标识符

ManagedThreadId是认同线程的唯一标识符,程序于大部情下都是经Thread.ManagedThreadId来识别线程的。而Name是一个但转移值,在默认时候,Name为一个空值
Null,开发人士可以因而序设置线程的名,但就仅仅是一个支援力量。

 

2.1.2 线程的事先级别

.NET为线程设置了Priority属性来定义线程执行之预级别,里面含有5独选项,其中Normal是默认值。除非系统有特殊要求,否则不应有无设置线程的先行级别。

成员名称 说明
Lowest 可以将 Thread 安排在具有任何其他优先级的线程之后。      
BelowNormal 可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。      
Normal 默认选择。可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有 BelowNormal 优先级的线程之前。 
AboveNormal 可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。      
Highest 可以将 Thread 安排在具有任何其他优先级的线程之前。      

 

2.1.3 线程的状态

透过ThreadState可以检测线程是地处Unstarted、Sleeping、Running
等等状态,它比 IsAlive 属性能提供再多的特定音讯。

前方说了,一个应用程序域中或者连多独及下文,而因而CurrentContext可以得到线程当前的上下文。

CurrentThread是绝常用之一个性能,它是用于获取当前运作的线程。

 

2.1.4 System.Threading.Thread的方法

Thread
中概括了大半独办法来控制线程的创制、挂于、截止、销毁,将来来的例子中会时常以。

方法名称 说明
Abort()     终止本线程。
GetDomain() 返回当前线程正在其中运行的当前域。
GetDomainId() 返回当前线程正在其中运行的当前域Id。
Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。
Join() 已重载。 阻塞调用线程,直到某个线程终止时为止。
Resume() 继续运行已挂起的线程。
Start()   执行本线程。
Suspend() 挂起当前线程,如果当前线程已属于挂起状态则此不起作用
Sleep()   把正在运行的线程挂起一段时间。

 

2.1.5 开发实例

以下这事例,就是经Thread显示当前线程新闻

图片 2😉

 1         static void Main(string[] args)
 2         {
 3             Thread thread = Thread.CurrentThread;
 4             thread.Name = "Main Thread";
 5             string threadMessage = string.Format("Thread ID:{0}\n    Current AppDomainId:{1}\n    "+
 6                 "Current ContextId:{2}\n    Thread Name:{3}\n    "+
 7                 "Thread State:{4}\n    Thread Priority:{5}\n",
 8                 thread.ManagedThreadId, Thread.GetDomainID(), Thread.CurrentContext.ContextID,
 9                 thread.Name, thread.ThreadState, thread.Priority);
10             Console.WriteLine(threadMessage);
11             Console.ReadKey();
12         }

图片 3😉

 

运转结果

图片 4

 

2.2  System.Threading 命名空间

在System.Threading命名空间内提供多单道来构建多线程应用程序,其中ThreadPool与Thread是多线程开发中但是常用到的,在.NET中特别设定了一个CLR线程池专门用来管理线程的运转,这一个CLR线程池正是通过ThreadPool类来治本。而Thread是治本线程的顶直接格局,下边几乎节以详细介绍有关内容。

类     说明
AutoResetEvent 通知正在等待的线程已发生事件。无法继承此类。
ExecutionContext 管理当前线程的执行上下文。无法继承此类。
Interlocked 为多个线程共享的变量提供原子操作。
Monitor 提供同步对对象的访问的机制。
Mutex 一个同步基元,也可用于进程间同步。
Thread 创建并控制线程,设置其优先级并获取其状态。
ThreadAbortException 在对 Abort 方法进行调用时引发的异常。无法继承此类。
ThreadPool 提供一个线程池,该线程池可用于发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。
Timeout 包含用于指定无限长的时间的常数。无法继承此类。
Timer 提供以指定的时间间隔执行方法的机制。无法继承此类。
WaitHandle 封装等待对共享资源的独占访问的操作系统特定的对象。

当System.Threading中的包含了下表中之大两只常用委托,其中ThreadStart、ParameterizedThreadStart是最常用到的委托。
由ThreadStart生成的线程是极端直白的章程,但出于ThreadStart所大成并无吃线程池管理。
而ParameterizedThreadStart是为异步触发带参数的法要要的,在生同样省用为我们逐一细说。

委托 说明
ContextCallback 表示要在新上下文中调用的方法。
ParameterizedThreadStart 表示在 Thread 上执行的方法。
ThreadExceptionEventHandler 表示将要处理 Application 的 ThreadException 事件的方法。
ThreadStart 表示在 Thread 上执行的方法。
TimerCallback 表示处理来自 Timer 的调用的方法。
WaitCallback 表示线程池线程要执行的回调方法。
WaitOrTimerCallback 表示当 WaitHandle 超时或终止时要调用的方法。

 

2.3 线程的管制章程

由此ThreadStart来创制一个初线程是极端直白的法门,但如此创设出来的线程相比难以管理,假如创立了多之线程反而会吃系统的属性下载。有见与那多少个,.NET为线程管理特别设置了一个CLR线程池,使用CLR线程池系统可以更合理地保管线程的采取。所有请求的服务都可以运行为线程池中,当运行了时线程便会回归至线程池。通过安装,能控制线程池的太特别线程数量,在伸手过线程最丰硕价值通常,线程池能遵照操作的先级别来实施,让有操作处于等候状态,待无线程回归时又履行操作。

基础知识就吧大家介绍到这里,上面将详细介绍多线程的出。

 

 

回去目录

老三、以ThreadStart情势贯彻多线程

3.1 使用ThreadStart委托

此地先以一个例子显示一下差不多线程带来的功利,首先以Message类中建立一个方法ShowMessage(),里面显示了如今运作线程的Id,并选拔Thread.Sleep(int
)
方法模拟部分工作。在main()中经ThreadStart委托绑定Message对象的ShowMessage()方法,然后经过Thread.Start()执行异步方法。

图片 5😉

 1       public class Message  2       {  3           public void ShowMessage()  4           {  5               string message = string.Format("Async threadId is :{0}",  6                                               Thread.CurrentThread.ManagedThreadId);  7               Console.WriteLine(message);  8     9               for (int n = 0; n < 10; n++) 10               { 11                   Thread.Sleep(300);    12                   Console.WriteLine("The number is:" + n.ToString());  13               } 14           } 15       } 16    17       class Program 18       { 19           static void Main(string[] args) 20           { 21               Console.WriteLine("Main threadId is:"+ 22                                 Thread.CurrentThread.ManagedThreadId); 23               Message message=new Message(); 24               Thread thread = new Thread(new ThreadStart(message.ShowMessage)); 25               thread.Start(); 26               Console.WriteLine("Do something ..........!"); 27               Console.WriteLine("Main thread working is complete!"); 28                29           } 30       }

图片 6😉

求留心运行结果,在调用Thread.Start()方法后,系统因为异步格局运行Message.ShowMessage(),而主线程的操作是继续执行的,在Message.ShowMessage()完成前,主线程已成功有的操作。

图片 7

 

3.2 使用ParameterizedThreadStart委托

ParameterizedThreadStart委托以及ThreadStart委托相当相似,但ParameterizedThreadStart委托是面向带参数方法的。注意ParameterizedThreadStart
对应措施的参数为object,此参数好吗一个价对象,也足以呢一个自定义对象。

图片 8😉

 1     public class Person
 2     {
 3         public string Name
 4         {
 5             get;
 6             set;
 7         }
 8         public int Age
 9         {
10             get;
11             set;
12         }
13     }
14 
15     public class Message
16     {
17         public void ShowMessage(object person)
18         {
19             if (person != null)
20             {
21                 Person _person = (Person)person;
22                 string message = string.Format("\n{0}'s age is {1}!\nAsync threadId is:{2}",
23                     _person.Name,_person.Age,Thread.CurrentThread.ManagedThreadId);
24                 Console.WriteLine(message);
25             }
26             for (int n = 0; n < 10; n++)
27             {
28                 Thread.Sleep(300);   
29                 Console.WriteLine("The number is:" + n.ToString()); 
30             }
31         }
32     }
33 
34     class Program
35     {
36         static void Main(string[] args)
37         {     
38             Console.WriteLine("Main threadId is:"+Thread.CurrentThread.ManagedThreadId);
39             
40             Message message=new Message();
41             //绑定带参数的异步方法
42             Thread thread = new Thread(new ParameterizedThreadStart(message.ShowMessage));
43             Person person = new Person();
44             person.Name = "Jack";
45             person.Age = 21;
46             thread.Start(person);  //启动异步线程 
47             
48             Console.WriteLine("Do something ..........!");
49             Console.WriteLine("Main thread working is complete!");
50              
51         }
52     }

图片 9😉

运转结果:

图片 10

 

3.3 前台线程与后台线程

只顾上述三个例都不曾应用Console.ReadKey(),但系统如故会待异步线程完成后才会了。这是为用Thread.Start()启动的线程默认为前台线程,而网要待所有前台线程运行了后,应用程序域才会自动卸载。

当第二节曾经介绍过线程Thread有一个性能IsBackground,通过将此属性设置为true,就可拿线程设置也后台线程!这时应用程序域将在主线程完成时便于卸载,而无碰面等待异步线程的运作。

 

3.4 挂于线程

以等待其他后台线程完成后又结主线程,就得接纳Thread.Sleep()方法。

图片 11😉

 1     public class Message  2     {  3         public void ShowMessage()  4         {  5             string message = string.Format("\nAsync threadId is:{0}",  6                                            Thread.CurrentThread.ManagedThreadId);  7             Console.WriteLine(message);  8             for (int n = 0; n < 10; n++)  9             { 10                 Thread.Sleep(300); 11                 Console.WriteLine("The number is:" + n.ToString()); 12             } 13         } 14     } 15  16     class Program 17     { 18         static void Main(string[] args) 19         {      20             Console.WriteLine("Main threadId is:"+ 21                               Thread.CurrentThread.ManagedThreadId); 22              23             Message message=new Message(); 24             Thread thread = new Thread(new ThreadStart(message.ShowMessage)); 25             thread.IsBackground = true; 26             thread.Start(); 27              28             Console.WriteLine("Do something ..........!"); 29             Console.WriteLine("Main thread working is complete!"); 30             Console.WriteLine("Main thread sleep!"); 31             Thread.Sleep(5000); 32         } 33     }

图片 12😉

运行结果如下,此时应用程序域将当主线程运行5秒后自行终止

图片 13

 

唯独系统不可能预知异步线程需要周转的时日,所以用经过Thread.Sleep(int)阻塞主线程并无是一个吓的化解格局。有见与这,.NET专门为等待异步线程完成支付了外一个方法thread.Join()。把地点例子中之终极一尽Thread.Sleep(5000)修改为
thread.Join() 就可知管主线程在异步线程thread运行了后才汇合告一段落。

 

3.5 Suspend 与 Resume (慎用)

Thread.Suspend()与 Thread.Resume()是以Framework1.0
就都存在的从来方法了,它们各自可挂于、復苏线程。但在Framework2.0中纵然早已领会排斥这片个法子。这是以一旦某个线程占用了就有的资源,再下Suspend()使线程长期处在挂于状态,当以任何线程调用这一个资源的时尽管晤面挑起死锁!所以在未曾必要的情事下该制止使这一点儿个模式。

 

3.6 终止线程

比方想停在运作的线程,可以用Abort()方法。在应用Abort()的下,将抓住一个特别非凡ThreadAbortException 。 若想当线程终止前复苏线程的推行,可以以抓获分外后
,在catch(ThreadAbortException ex){…}
中调用Thread.ResetAbort()撤除终止。
而使用Thread.Join()可以管应用程序域等待异步线程截至后才平息运行。

图片 14😉

 1          static void Main(string[] args)
 2          {
 3              Console.WriteLine("Main threadId is:" +
 4                                Thread.CurrentThread.ManagedThreadId);
 5  
 6              Thread thread = new Thread(new ThreadStart(AsyncThread));
 7              thread.IsBackground = true;
 8              thread.Start();
 9              thread.Join();
10  
11          }     
12          
13          //以异步方式调用
14          static void AsyncThread()
15          {
16              try
17              {
18                  string message = string.Format("\nAsync threadId is:{0}",
19                     Thread.CurrentThread.ManagedThreadId);
20                  Console.WriteLine(message);
21  
22                  for (int n = 0; n < 10; n++)
23                  {
24                      //当n等于4时,终止线程
25                      if (n >= 4)
26                      {
27                          Thread.CurrentThread.Abort(n);
28                      }
29                      Thread.Sleep(300);
30                      Console.WriteLine("The number is:" + n.ToString());
31                  }
32              }
33              catch (ThreadAbortException ex)
34              {
35                  //输出终止线程时n的值
36                  if (ex.ExceptionState != null)
37                      Console.WriteLine(string.Format("Thread abort when the number is: {0}!", 
38                                                       ex.ExceptionState.ToString()));
39                 
40                  //取消终止,继续执行线程
41                  Thread.ResetAbort();
42                  Console.WriteLine("Thread ResetAbort!");
43              }
44  
45              //线程结束
46              Console.WriteLine("Thread Close!");
47          }

图片 15😉

运作结果如下

图片 16

 

 

回来目录

季、CLR线程池的劳引力线程

4.1 关于CLR线程池

动ThreadStart与ParameterizedThreadStart建立新线程卓殊简单,但透过之措施创设的线程难让管理,若建立了多之线程反而会影响系的性质。
有显现与这,.NET引入CLR线程池这多少个定义。CLR线程池并无会合在CLR起头化的早晚霎时起线程,而是于应用程序要创制线程来实施任务时,线程池才伊始化一个线程。线程的开首化与另的线程一样。在完成任务后,该线程不相会活动销毁,而是以悬挂于的状态重临到线程池。直到应用程序再次向线程池发出请求时,线程池里悬挂于的线程就汇合重激活执行任务。这样既节约了起线程所导致的性质损耗,也得以给多单任务数用同一线程,从而以应用程序生存期内节约大量开发。

注意透过CLR线程池所建之线程总是默认为后台线程,优先级数为ThreadPriority.Normal。

 

4.2 工作者线程与I/O线程

CLR线程池分为工作者线程(workerThreads)与I/O线程 (completionPortThreads)
二种,工作者线程是至关重要用作管理CLR内部对象的运行,I/O(Input/Output)
线程顾名思义是用于和外表系统交流新闻,IO线程的底细将以产一致省详细表明。

透过ThreadPool.Get马克斯(Max)(out int workerThreads,out int
completionPortThreads )和 ThreadPool.Set马克斯(Max)( int workerThreads, int
completionPortThreads)两单主意好独家读取和安装CLR线程池中工作者线程与I/O线程的最为丰裕线程数。在Framework2.0吃最为要命线程默看25*CPU数,在Framewok3.0、4.0中尽深线程数默认为250*CPU数,在日前
I3,I5,I7 CPU出现继,线程池的极端可怜价值一般默认为1000、2000。
若想测试线程池中来略之线程正在投入使用,可以经ThreadPool.GetAvailableThreads(
out int workerThreads,out int
completionPortThreads ) 方法。

用CLR线程池的劳力线程一般爆发一定量种植艺术,一凡一直通过
ThreadPool.QueueUserWorkItem() 方法,二是透过信托,下面用各种细说。

 

4.3 通过QueueUserWorkItem启动工作者线程

ThreadPool线程池中富含有半点单静态方法能够一直开行工作者线程: 一乎
ThreadPool.QueueUserWorkItem(WaitCallback) 二啊
ThreadPool.QueueUserWorkItem(WaitCallback,Object) 

先行将WaitCallback委托指向一个蕴含Object参数的无重临值方法,再运
ThreadPool.QueueUserWorkItem(WaitCallback)
就可以异步启动之方法,此时异步方法的参数为视为null 。

图片 17😉

 1     class Program  2     {  3         static void Main(string[] args)  4         {  5             //把CLR线程池的最大值设置为1000  6             ThreadPool.SetMaxThreads(1000, 1000);  7             //显示主线程启动时线程池信息  8             ThreadMessage("Start");  9             //启动工作者线程 10             ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback)); 11             Console.ReadKey(); 12         } 13          14         static void AsyncCallback(object state) 15         { 16             Thread.Sleep(200); 17             ThreadMessage("AsyncCallback"); 18             Console.WriteLine("Async thread do work!"); 19         } 20  21         //显示线程现状 22         static void ThreadMessage(string data) 23         { 24             string message = string.Format("{0}\n  CurrentThreadId is {1}", 25                  data, Thread.CurrentThread.ManagedThreadId); 26             Console.WriteLine(message); 27         } 28     }

图片 18😉

运作结果

图片 19

 

应用 ThreadPool.QueueUserWorkItem(WaitCallback,Object)
方法可将object对象作为参数传送到回调函数中。
下面例子中不怕是拿一个string对象作为参数发送到回调函数当中。

图片 20😉

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //把线程池的最大值设置为1000
 6             ThreadPool.SetMaxThreads(1000, 1000);
 7           
 8             ThreadMessage("Start");
 9             ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback),"Hello Elva");
10             Console.ReadKey();
11         }
12 
13         static void AsyncCallback(object state)
14         {
15             Thread.Sleep(200);
16             ThreadMessage("AsyncCallback");
17 
18             string data = (string)state;
19             Console.WriteLine("Async thread do work!\n"+data);
20         }
21 
22         //显示线程现状
23         static void ThreadMessage(string data)
24         {
25             string message = string.Format("{0}\n  CurrentThreadId is {1}",
26                  data, Thread.CurrentThread.ManagedThreadId);
27             Console.WriteLine(message);
28         }
29     }

图片 21😉

运行结果

图片 22

 

由此ThreadPool.QueueUserWorkItem启动工作者线程即使是好,但WaitCallback委托指向的得是一个含Object参数的无再次回到值方法,这的是均等栽范围。若方法需要发重回值,或者隐含两个参数,这将多费周折。有显现与这一个,.NET提供了别一样种植艺术去立工作者线程,这便是委托。

 

4.4  委托类       

动CLR线程池中之劳力线程,最灵最常用之方就是运用委托的异步方法,在这些先简单介绍一下委托类。

当定义委托后,.NET就会自行创造一个代表该信托的近乎,下边能够用反射模式示委托类的方成员(对反射出趣味之朋友可以优先参考一下“.NET基础篇——反射的微妙”)

图片 23😉

 1     class Program  2     {  3         delegate void MyDelegate();  4   5         static void Main(string[] args)  6         {  7             MyDelegate delegate1 = new MyDelegate(AsyncThread);  8             //显示委托类的几个方法成员       9             var methods=delegate1.GetType().GetMethods(); 10             if (methods != null) 11                 foreach (MethodInfo info in methods) 12                     Console.WriteLine(info.Name); 13             Console.ReadKey(); 14          } 15      }

图片 24😉

委托类包括以下几单第一措施

图片 25

图片 26😉

1     public class MyDelegate:MulticastDelegate
2     {
3         public MyDelegate(object target, int methodPtr);
4         //调用委托方法
5         public virtual void Invoke();
6         //异步委托
7         public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
8         public virtual void EndInvoke(IAsyncResult result);
9     }

图片 27😉

当调用Invoke()方法时,对诺之委托的拥有方都谋面为执行。而BeginInvoke与EndInvoke则援助委托方的异步调用,由BeginInvoke启动之线程都属于CLR线程池中的劳力线程,在底下将详细表明。

 

4.5  利用BeginInvoke与EndInvoke完成异步委托方

先是成立一个信托对象,通过IAsyncResult BeginInvoke(string
name,AsyncCallback callback,object state) 异步调用委托方,BeginInvoke
方法除最后之点滴个参数外,其余参数依然同措施参数相呼应的。通过 BeginInvoke
方法将赶回一个落实了 System.IAsyncResult
接口的目的,之后就足以动用EndInvoke(IAsyncResult )
方法就得收异步操作,获取委托的运作结果。

图片 28😉

 1     class Program  2     {  3         delegate string MyDelegate(string name);  4   5         static void Main(string[] args)  6         {  7             ThreadMessage("Main Thread");  8               9             //建立委托 10             MyDelegate myDelegate = new MyDelegate(Hello); 11             //异步调用委托,获取计算结果 12             IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null); 13             //完成主线程其他工作 14             .............  15             //等待异步方法完成,调用EndInvoke(IAsyncResult)获取运行结果 16             string data=myDelegate.EndInvoke(result); 17             Console.WriteLine(data); 18              19             Console.ReadKey(); 20         } 21  22         static string Hello(string name) 23         { 24             ThreadMessage("Async Thread"); 25             Thread.Sleep(2000);            //虚拟异步工作 26             return "Hello " + name; 27         } 28  29         //显示当前线程 30         static void ThreadMessage(string data) 31         { 32             string message = string.Format("{0}\n  ThreadId is:{1}", 33                    data,Thread.CurrentThread.ManagedThreadId); 34             Console.WriteLine(message); 35         } 36     }

图片 29😉

运行结果

图片 30

 

4.6  善用IAsyncResult

在以上例子中得以看见,假若在接纳myDelegate.BeginInvoke后当即调用myDelegate.EndInvoke,这在异步线程未到位工作先主线程将处于阻塞状态,等到异步线程为止得总结结果后,主线程才会连续工作,这显著无法出示出多线程的优势。此时可好利用IAsyncResult
提高主线程的干活性质,IAsyncResult有以下成员:

图片 31😉

1 public interface IAsyncResult
2 {
3     object AsyncState {get;}            //获取用户定义的对象,它限定或包含关于异步操作的信息。
4     WailHandle AsyncWaitHandle {get;}   //获取用于等待异步操作完成的 WaitHandle。
5     bool CompletedSynchronously {get;}  //获取异步操作是否同步完成的指示。
6     bool IsCompleted {get;}             //获取异步操作是否已完成的指示。
7 }

图片 32😉

透过轮询情势,使用IsCompleted属性判断异步操作是否就,这样以异步操作不成功前就是足以吃主线程执行此外的做事。

图片 33😉

 1     class Program  2     {  3         delegate string MyDelegate(string name);  4   5         static void Main(string[] args)  6         {  7             ThreadMessage("Main Thread");  8               9             //建立委托 10             MyDelegate myDelegate = new MyDelegate(Hello); 11             //异步调用委托,获取计算结果 12             IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null); 13             //在异步线程未完成前执行其他工作 14             while (!result.IsCompleted) 15             { 16                 Thread.Sleep(200);      //虚拟操作 17                 Console.WriteLine("Main thead do work!"); 18             } 19             string data=myDelegate.EndInvoke(result); 20             Console.WriteLine(data); 21              22             Console.ReadKey(); 23         } 24  25         static string Hello(string name) 26         { 27             ThreadMessage("Async Thread"); 28             Thread.Sleep(2000); 29             return "Hello " + name; 30         } 31  32         static void ThreadMessage(string data) 33         { 34             string message = string.Format("{0}\n  ThreadId is:{1}", 35                    data,Thread.CurrentThread.ManagedThreadId); 36             Console.WriteLine(message); 37         } 38     }

图片 34😉

运转结果:

图片 35

 

除开本条之外,也得以用WailHandle完成同样的行事,WaitHandle里面含有一个措施WaitOne(int
timeout),它好判委托是否成功工作,在办事未得前主线程可以继承其他工作。运行上面代码可抱与祭
IAsyncResult.IsCompleted 同样的结果,而且又简短方便 。

图片 36😉

 1 namespace Test
 2 {
 3     class Program
 4     {
 5         delegate string MyDelegate(string name);
 6 
 7         static void Main(string[] args)
 8         {
 9             ThreadMessage("Main Thread");
10             
11             //建立委托
12             MyDelegate myDelegate = new MyDelegate(Hello);
13  
14             //异步调用委托,获取计算结果
15             IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);
16             
17             while (!result.AsyncWaitHandle.WaitOne(200))
18             {
19                 Console.WriteLine("Main thead do work!");
20             }
21             string data=myDelegate.EndInvoke(result);
22             Console.WriteLine(data);
23             
24             Console.ReadKey();
25         }
26 
27         static string Hello(string name)
28         {
29             ThreadMessage("Async Thread");
30             Thread.Sleep(2000);
31             return "Hello " + name;
32         }
33 
34         static void ThreadMessage(string data)
35         {
36             string message = string.Format("{0}\n  ThreadId is:{1}",
37                    data,Thread.CurrentThread.ManagedThreadId);
38             Console.WriteLine(message);
39         }
40     }

图片 37😉

当要监视多独运行目标的时光,使用IAsyncResult.WaitHandle.WaitOne可虽然差不达用场了。
幸好.NET为WaitHandle准备了其它七只静态方法:WaitAny(waitHandle[],
int)与WaitAll (waitHandle[] , int)。
其中WaitAll在守候所有waitHandle完成后更回去一个bool值。
而WaitAny是等待之中一个waitHandle完成后固然回一个int,这一个int是意味着就做到waitHandle在waitHandle[]被之数组索引。
下边就是采用WaitAll的例证,运行结果以及祭 IAsyncResult.IsCompleted
相同。

图片 38😉

 1     class Program  2     {  3         delegate string MyDelegate(string name);  4   5         static void Main(string[] args)  6         {  7             ThreadMessage("Main Thread");  8               9             //建立委托 10             MyDelegate myDelegate = new MyDelegate(Hello); 11   12             //异步调用委托,获取计算结果 13             IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null); 14  15             //此处可加入多个检测对象 16             WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ }; 17             while (!WaitHandle.WaitAll(waitHandleList,200)) 18             { 19                 Console.WriteLine("Main thead do work!"); 20             } 21             string data=myDelegate.EndInvoke(result); 22             Console.WriteLine(data); 23              24             Console.ReadKey(); 25         } 26  27         static string Hello(string name) 28         { 29             ThreadMessage("Async Thread"); 30             Thread.Sleep(2000); 31             return "Hello " + name; 32         } 33  34         static void ThreadMessage(string data) 35         { 36             string message = string.Format("{0}\n  ThreadId is:{1}", 37                    data,Thread.CurrentThread.ManagedThreadId); 38             Console.WriteLine(message); 39         } 40     }

图片 39😉

 

4.7 回调函数

拔取轮询模式来检测异步方法的状态分外麻烦,而且效能不高,有呈现与这多少个,.NET为
IAsyncResult BeginInvoke(AsyncCallback ,
object)准备了一个回调函数。使用 AsyncCallback
就得绑定一个艺术作为回调函数,回调函数必须是带来参数 IAsyncResult
且不论再次回到值的道: void AsycnCallbackMethod(IAsyncResult result)
。在BeginInvoke方法就后,系统就会调用AsyncCallback所绑定的回调函数,最后回调函数中调用
XXX EndInvoke(IAsyncResult result)
就好收异步方法,它的回到值类型与寄托的再次来到值一致。

图片 40😉

 1     class Program
 2     {
 3         delegate string MyDelegate(string name);
 4 
 5         static void Main(string[] args)
 6         {
 7             ThreadMessage("Main Thread");
 8 
 9             //建立委托
10             MyDelegate myDelegate = new MyDelegate(Hello);
11             //异步调用委托,获取计算结果
12             myDelegate.BeginInvoke("Leslie", new AsyncCallback(Completed), null);
13             //在启动异步线程后,主线程可以继续工作而不需要等待
14             for (int n = 0; n < 6; n++)
15                 Console.WriteLine("  Main thread do work!");
16             Console.WriteLine("");
17 
18             Console.ReadKey();
19         }
20 
21         static string Hello(string name)
22         {
23             ThreadMessage("Async Thread");
24             Thread.Sleep(2000);             \\模拟异步操作
25             return "\nHello " + name;
26         }
27 
28         static void Completed(IAsyncResult result)
29         {
30             ThreadMessage("Async Completed");
31 
32             //获取委托对象,调用EndInvoke方法获取运行结果
33             AsyncResult _result = (AsyncResult)result;
34             MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate;
35             string data = myDelegate.EndInvoke(_result);
36             Console.WriteLine(data);
37         }
38 
39         static void ThreadMessage(string data)
40         {
41             string message = string.Format("{0}\n  ThreadId is:{1}",
42                    data, Thread.CurrentThread.ManagedThreadId);
43             Console.WriteLine(message);
44         }
45     }

图片 41😉

可看看,主线在调用BeginInvoke方法可以继续执行其他命令,而任由需重新伺机了,这的比采用轮询情势判断异步方法是否做到再发出优势。
在异步方法执行到位后将会见调用AsyncCallback所绑定的回调函数,注意一点,回调函数仍然是于异步线程中举办,这样即便无相会影响主线程的周转,这也采纳回调函数最值得青昧的地点。
在回调函数中生一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就可以经过
AsyncResult.AsyncDelegate 获取原委托,再拔取EndInvoke方法取得总计结果。
运行结果如下:

图片 42

设想啊回调函数传送一些表音讯,就好下BeginInvoke(AsyncCallback,object)的末段一个参数object,它同意外部为回调函数输入外项目的参数。只待以回调函数中利用
AsyncResult.AsyncState 就得收获object对象。

图片 43😉

 1     class Program  2     {  3         public class Person  4         {  5             public string Name;  6             public int Age;  7         }  8   9         delegate string MyDelegate(string name); 10  11         static void Main(string[] args) 12         { 13             ThreadMessage("Main Thread"); 14  15             //建立委托 16             MyDelegate myDelegate = new MyDelegate(Hello); 17              18             //建立Person对象 19             Person person = new Person(); 20             person.Name = "Elva"; 21             person.Age = 27; 22              23             //异步调用委托,输入参数对象person, 获取计算结果 24             myDelegate.BeginInvoke("Leslie", new AsyncCallback(Completed), person);             25            26             //在启动异步线程后,主线程可以继续工作而不需要等待 27             for (int n = 0; n < 6; n++) 28                 Console.WriteLine("  Main thread do work!"); 29             Console.WriteLine(""); 30  31             Console.ReadKey(); 32         } 33  34         static string Hello(string name) 35         { 36             ThreadMessage("Async Thread"); 37             Thread.Sleep(2000); 38             return "\nHello " + name; 39         } 40  41         static void Completed(IAsyncResult result) 42         { 43             ThreadMessage("Async Completed"); 44  45             //获取委托对象,调用EndInvoke方法获取运行结果 46             AsyncResult _result = (AsyncResult)result; 47             MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate; 48             string data = myDelegate.EndInvoke(_result); 49             //获取Person对象 50             Person person = (Person)result.AsyncState; 51             string message = person.Name + "'s age is " + person.Age.ToString(); 52  53             Console.WriteLine(data+"\n"+message); 54         } 55  56         static void ThreadMessage(string data) 57         { 58             string message = string.Format("{0}\n  ThreadId is:{1}", 59                    data, Thread.CurrentThread.ManagedThreadId); 60             Console.WriteLine(message); 61         } 62     }

图片 44😉

运行结果:

图片 45

 

关于I/O线程、SqlCommand多线程查询、PLINQ、定时器与锁之始末将以C#综述揭秘——细说多线程(下)中详尽介绍。
对 .NET 开发有趣味的仇人欢迎插足QQ群:230564952
共同钻探 !

回目录

C#综述揭秘

经改注册表建立Windows自定义协议
Entity Framework
并发处理详解
前述进程、应用程序域与上下文

细说基本上线程(上)

细说多线程(下) 前述事务深远解析委托以及事件

 

笔者:风尘浪子 http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html

原创著作,转载时请评释作者和出处

 

 

 

分类:
C#归咎揭秘

相关文章