差不多线程(上)

引言

正文重要由线程的根底因而法,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只接纳,其中诺玛l是默认值。除非系统发出特殊要求,否则不应该无设置线程的优先级别。

成员名称 说明
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呈现当前线程音讯

 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 2

 

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

 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       }

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

Ajax 3

 

3.2 使用ParameterizedThreadStart委托

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

 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 4

 

3.3 前台线程与后台线程

留意上述两单例证都未曾选用Console.ReadKey(),但系统依旧会等异步线程完成后才晤面结。那是因以Thread.Start()启动的线程默认为前台线程,而网要待所有前台线程运行了后,应用程序域才会面自行卸载。

当第二节曾经介绍过线程Thread有一个性能IsBackground,通过把此属性设置也true,就可以将线程设置为后台线程!这时应用程序域将当主线程完成时虽深受卸载,而未碰面等异步线程的运行。

 

3.4 挂于线程

以等待其他后台线程完成后重新结主线程,就可动用Thread.Sleep()方法。

 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     }

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

Ajax 5

 

但系统无法预知异步线程需要周转的时光,所以用经过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()能够包应用程序域等待异步线程截止后才停下运行。

 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 6

 

 

回去目录

季、CLR线程池的工作者线程

4.1 关于CLR线程池

运ThreadStart与ParameterizedThreadStart建立新线程十分简单,但经是道创设之线程难被管理,若建立了多的线程反而会潜移默化系的性。
生表现与这,.NET引入CLR线程池这些概念。CLR线程池并无会合当CLR起始化的当儿登时起线程,而是在应用程序要开创线程来推行任务时,线程池才初步化一个线程。线程的起先化与其它的线程一样。在形成任务后,该线程不会晤活动销毁,而是因为悬挂于的状态重回到线程池。直到应用程序再一次向线程池发出请求时,线程池里悬挂于的线程就会另行激活执行任务。这样既节省了树立线程所招的习性损耗,也得以吃大多独任务数用同一线程,从而以应用程序生存期内节约大量支出。

注意经过CLR线程池所确立之线程总是默认为后台线程,优先级数为ThreadPriority.诺玛l。

 

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 。

 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 7

 

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

 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 8

 

通过ThreadPool.QueueUserWorkItem启动工作者线程即便是利,但WaitCallback委托指向的必须是一个带有Object参数的无再次来到值方法,这的确是如出一辙栽限制。若方法要发出再次来到值,或者隐含多独参数,这将多费周折。有表现与这,.NET提供了其他一样种植方法去立工作者线程,这便是委托。

 

4.4  委托类       

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

当定义委托后,.NET就会晤活动成立一个象征该信托的类似,下面可以就此反射形式示委托类的主意成员(对反射出趣味之爱人可预先参考一下“.NET基础篇——反射的奥妙”)

 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 9

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     }

当调用Invoke()方法时,对许以此委托的拥有方还会师吃实施。而BeginInvoke与EndInvoke则援助委托方的异步调用,由BeginInvoke启动的线程都属CLR线程池中之劳重力线程,在脚用详细表明。

 

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

首先建立一个托对象,通过IAsyncResult BeginInvoke(string
name,AsyncCallback callback,object state) 异步调用委托方,BeginInvoke
方法除最后之星星个参数外,此外参数如故与措施参数相对应之。通过 BeginInvoke
方法以回到一个贯彻了 System.IAsyncResult
接口的靶子,之后虽足以使用EndInvoke(IAsyncResult )
方法就是得终结异步操作,获取委托的运作结果。

 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 10

 

4.6  善用IAsyncResult

每当上述例子中可以看见,假使当使myDelegate.BeginInvoke后即时调用myDelegate.EndInvoke,这以异步线程未得工作先主线程将处于阻塞状态,等到异步线程截止得总括结果后,主线程才可以继续工作,这明摆着力不从心显示出多线程的优势。此时可好利用IAsyncResult
提升主线程的干活性质,IAsyncResult有以下成员:

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

透过轮询模式,使用IsCompleted属性判断异步操作是否好,那样以异步操作不成功前就是可以于主线程执行此外的行事。

 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 11

 

除却本条之外,也足以以WailHandle完成同样的办事,WaitHandle里面含有一个艺术WaitOne(int
timeout),它美观清委托是否成功工作,在办事不得前主线程可以继承其他干活。运行下边代码可得同行使
IAsyncResult.IsCompleted 同样的结果,而且又简便方便 。

 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     }

当要监视四个运行目的的时候,使用IAsyncResult.WaitHandle.WaitOne可就是派不达用了。
幸好.NET也WaitHandle准备了此外两单静态方法:WaitAny(waitHandle[],
int)与WaitAll (waitHandle[] , int)。
里WaitAll在等所有waitHandle完成后再返一个bool值。
倘若WaitAny是伺机中一个waitHandle完成后虽重临一个int,这一个int是象征已好waitHandle在waitHandle[]蒙的数组索引。
下就是使用WaitAll的事例,运行结果跟以 IAsyncResult.IsCompleted
相同。

 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     }

 

4.7 回调函数

Ajax,动用轮询模式来检测异步方法的状态异常累,而且效能不愈,有表现与这,.NET也
IAsyncResult BeginInvoke(AsyncCallback ,
object)准备了一个回调函数。使用 AsyncCallback
就可绑定一个办法作为回调函数,回调函数必须是带动参数 IAsyncResult
且无重回值的章程: void AsycnCallbackMethod(IAsyncResult result)
。在BeginInvoke方法就后,系统即会调用AsyncCallback所绑定的回调函数,最终回调函数中调用
XXX EndInvoke(IAsyncResult result)
就可以停止异步方法,它的返值类型与信托的重回值一致。

 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     }

得视,主线在调用BeginInvoke方法可继续执行其他命令,而任由需再度等了,这确实比使用轮询格局判断异步方法是否做到还爆发优势。
每当异步方法执行好后拿会调用AsyncCallback所绑定的回调函数,注意一点,回调函数如故是在异步线程中举行,这样即使非会见潜移默化主线程的运作,这也利用回调函数最值得青昧的地点。
以回调函数中来一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就足以经过
AsyncResult.AsyncDelegate
获取原委托,再利用EndInvoke方法得到统计结果。
运转结果如下:

Ajax 12

假定想啊回调函数传送一些表音信,就可行使BeginInvoke(AsyncCallback,object)的末梢一个参数object,它同意外部为回调函数输入任何项目标参数。只需要在回调函数中利用
AsyncResult.AsyncState 就好拿走object对象。

 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 13

 

有关I/O线程、SqlCommand多线程查询、PLINQ、定时器与锁的情以当C#汇总揭秘——细说多线程(下)倍受详细介绍。
对 .NET 开发有趣味的朋友迎参加QQ群:230564952
共同探究 !

回到目录

C#归咎揭秘

由此改动注册表建立Windows自定义协议
Entity Framework
并发处理详解

前述进程、应用程序域与上下文

前述多线程(上)

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

 

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

原创随笔,转载时呼吁表明作者及出处

 

相关文章