Ajax[转]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主要用以存放线程的状态信息。

经过、应用程序域、线程的涉要下图,一个经过内得以概括多个应用程序域,也有包多单线程,线程也可以不断给多只应用程序域当中。但在与一个时刻,线程只见面处在一个应用程序域内。

 

Ajax 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显示当前线程信息

Ajax 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         }

Ajax 3😉

 

运行结果

Ajax 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()执行异步方法。

Ajax 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       }

Ajax 6😉

请留意运行结果,在调用Thread.Start()方法后,系统以异步方式运行Message.ShowMessage(),而主线程的操作是继续执行的,在Message.ShowMessage()完成前,主线程已做到有着的操作。

Ajax 7

 

3.2 使用ParameterizedThreadStart委托

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

Ajax 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     }

Ajax 9😉

运行结果:

Ajax 10

 

3.3 前台线程与后台线程

在意上述两个例证都没有采用Console.ReadKey(),但系统依然会待异步线程完成后才见面终结。这是盖以Thread.Start()启动之线程默认为前台线程,而系统必须待所有前台线程运行了晚,应用程序域才见面自动卸载。

每当第二节曾经介绍过线程Thread有一个性质IsBackground,通过把这属性设置为true,就得拿线程设置也后台线程!这时应用程序域将在主线程完成时就是受卸载,而休会见等待异步线程的运作。

 

3.4 挂于线程

为等待其他后台线程完成后更收主线程,就可动用Thread.Sleep()方法。

Ajax 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     }

Ajax 12😉

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

Ajax 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()可以保证应用程序域等待异步线程结束晚才已运行。

Ajax 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          }

Ajax 15😉

运转结果如下

Ajax 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.GetMax(out int workerThreads,out int
completionPortThreads )和 ThreadPool.SetMax( 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 。

Ajax 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     }

Ajax 18😉

运转结果

Ajax 19

 

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

Ajax 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     }

Ajax 21😉

运转结果

Ajax 22

 

由此ThreadPool.QueueUserWorkItem启动工作者线程虽然是有益,但WaitCallback委托指向的总得是一个分包Object参数的无返回值方法,这的确是一律种范围。若方法需要出归值,或者隐含多只参数,这将多费周折。有呈现与这个,.NET提供了另外一样栽方式去立工作者线程,那就是是托。

 

4.4  委托类       

下CLR线程池中之劳力线程,最灵最常用的法子尽管是用委托的异步方法,在此先简单介绍一下委托类。

当定义委托后,.NET就见面自动创建一个表示该信托的类,下面可以就此反射方式示委托类的法成员(对反射出趣味的爱人可预先参考一下“.NET基础篇——反射的神秘”)

Ajax 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      }

Ajax 24😉

委托类包括以下几单至关重要方法

Ajax 25

Ajax 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     }

Ajax 27😉

当调用Invoke()方法时,对诺以此委托的保有术还见面吃执行。而BeginInvoke与EndInvoke则支持委托方的异步调用,由BeginInvoke启动之线程都属CLR线程池中之劳力线程,在下面用详细说明。

 

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

率先建立一个寄托对象,通过IAsyncResult BeginInvoke(string
name,AsyncCallback callback,object state) 异步调用委托方,BeginInvoke
方法除最后的蝇头只参数外,其它参数都是同方参数相呼应之。通过 BeginInvoke
方法将回一个落实了 System.IAsyncResult
接口的对象,之后就可应用EndInvoke(IAsyncResult )
方法就是可以结束异步操作,获取委托的周转结果。

Ajax 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     }

Ajax 29😉

运转结果

Ajax 30

 

4.6  善用IAsyncResult

在上述例子中得以瞥见,如果以用myDelegate.BeginInvoke后随即调用myDelegate.EndInvoke,那以异步线程未到位工作先主线程将处于阻塞状态,等到异步线程结束得计算结果后,主线程才能够继续做事,这显然力不从心展示出多线程的优势。此时得好好利用IAsyncResult
提高主线程的劳作性质,IAsyncResult有以下成员:

Ajax 31😉

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

Ajax 32😉

经过轮询方式,使用IsCompleted属性判断异步操作是否就,这样于异步操作不到位前纵可吃主线程执行另外的劳作。

Ajax 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     }

Ajax 34😉

运行结果:

Ajax 35

 

除此之外此之外,也足以利用WailHandle完成同样的办事,WaitHandle里面富含有一个法WaitOne(int
timeout),它好判委托是否形成工作,在办事未到位前主线程可以继承其他干活。运行下面代码可得到同使用
IAsyncResult.IsCompleted 同样的结果,而且重简便方便 。

Ajax 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     }

Ajax 37😉

当要监视多个运行目标的当儿,使用IAsyncResult.WaitHandle.WaitOne可即便叫不达到用了。
幸好.NET为WaitHandle准备了另外两个静态方法:WaitAny(waitHandle[],
int)与WaitAll (waitHandle[] , int)。
其中WaitAll在待所有waitHandle完成后还返回一个bool值。
而WaitAny是等待之中一个waitHandle完成后即使回到一个int,这个int是代表曾到位waitHandle在waitHandle[]受到之数组索引。
下面就采取WaitAll的事例,运行结果与用 IAsyncResult.IsCompleted
相同。

Ajax 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     }

Ajax 39😉

 

4.7 回调函数

利用轮询方式来检测异步方法的状态好累,而且效率不赛,有见与这,.NET也
IAsyncResult BeginInvoke(AsyncCallback ,
object)准备了一个回调函数。使用 AsyncCallback
就足以绑定一个方式作为回调函数,回调函数必须是带来参数 IAsyncResult
且不论返回值的主意: void AsycnCallbackMethod(IAsyncResult result)
。在BeginInvoke方法好后,系统就见面调用AsyncCallback所绑定的回调函数,最后回调函数中调用
XXX EndInvoke(IAsyncResult result)
就好收异步方法,它的回到值类型与信托的返回值一致。

Ajax 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     }

Ajax 41😉

足看,主线在调用BeginInvoke方法可以继续执行其他命令,而不论需更等待了,这如实比用轮询方式判断异步方法是否就再有优势。
在异步方法执行好后用会见调用AsyncCallback所绑定的回调函数,注意一点,回调函数依然是以异步线程中实施,这样尽管不见面影响主线程的运行,这吗祭回调函数最值得青昧的地方。
在回调函数中出一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就好透过
AsyncResult.AsyncDelegate 获取原委托,再运EndInvoke方法获得计算结果。
运行结果如下:

Ajax 42

比方想为回调函数传送一些标信息,就可用BeginInvoke(AsyncCallback,object)的末尾一个参数object,它同意外部为回调函数输入任何项目的参数。只需要在回调函数中采用
AsyncResult.AsyncState 就好得到object对象。

Ajax 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     }

Ajax 44😉

运转结果:

Ajax 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#汇总揭秘

相关文章