AjaxC#综合揭秘——细说多线程(上)

转载:http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html\#undefined

引言

本文主要由线程的根基因此法,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主要用来存放线程的状态信息。

进程、应用程序域、线程的涉嫌要下图,一个经过内足以包括多个应用程序域,也发生包多单线程,线程也足以不停给多只应用程序域当中。但于与一个随时,线程只会处在一个应用程序域内。

   

 
是因为本文是因介绍多线程技术呢主题,对过程、应用程序域的牵线就交这个结束。关于进程、线程、应用程序域的技能,在”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 1

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

   

运转结果

Ajax 3

   

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 4

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 5

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

Ajax 6

   

3.2 使用ParameterizedThreadStart委托

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

Ajax 7

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 8

运行结果:

Ajax 9

   

3.3 前台线程与后台线程

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

在第二节曾经介绍过线程Thread有一个特性IsBackground,通过把这个属性设置为true,就好将线程设置也后台线程!这时应用程序域将当主线程完成时就吃卸载,而非见面等待异步线程的运行。

   

3.4 挂于线程

为等待其他后台线程完成后再收主线程,就得下Thread.Sleep()方法。

Ajax 10

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 11

运作结果如下,此时应用程序域将于主线程运行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()可以包应用程序域等待异步线程结束后才停下运行。

按 Ctrl+C 复制代码

Ajax 12

论 Ctrl+C 复制代码

运作结果如下

Ajax 13

   

   

归来目录

季、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 14

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 15

运行结果

   

采取 ThreadPool.QueueUserWorkItem(WaitCallback,Object)
方法好把object对象作为参数传送至回调函数中。
下例子中虽是拿一个string对象作为参数发送到回调函数当中。

Ajax 16

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 17

运作结果

Ajax 18

   

经过ThreadPool.QueueUserWorkItem启动工作者线程虽然是便民,但WaitCallback委托指向的必是一个含有Object参数的无返回值方法,这无疑是相同种植范围。若方法要发出返值,或者隐含多单参数,这将多费周折。有表现与这个,.NET提供了别样一样栽方式去立工作者线程,那即便是委托。

   

4.4  委托类       

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

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

Ajax 19

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 20

委托类包括以下几独至关重要艺术

Ajax 21

Ajax 22

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 23

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

   

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

率先成立一个托对象,通过IAsyncResult BeginInvoke(string
name,AsyncCallback callback,object state) 异步调用委托方,BeginInvoke
方法除最后之有限单参数外,其它参数都是跟方参数相呼应之。通过 BeginInvoke
方法将回到一个实现了 System.IAsyncResult
接口的目标,之后就足以行使EndInvoke(IAsyncResult )
方法就是得终结异步操作,获取委托的周转结果。

Ajax 24

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 25

运转结果

Ajax 26

   

4.6  善用IAsyncResult

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

Ajax 27

1 public interface IAsyncResult
2 {
3 object AsyncState {get;}
//获取用户定义之目标,它界定或含有关于异步操作的消息。
4 WailHandle AsyncWaitHandle {get;} //获取用于等待异步操作完的
WaitHandle。
5 bool CompletedSynchronously {get;}
//获取异步操作是否同步到位的指令。
6 bool IsCompleted {get;} //获取异步操作是否已形成的指令。
7 }

Ajax 28

透过轮询方式,使用IsCompleted属性判断异步操作是否成功,这样以异步操作不成功前纵足以为主线程执行另外的劳作。

Ajax 29

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 30

运行结果:

Ajax 31

   

除本条之外,也足以以WailHandleAjax完成同样的办事,WaitHandle里面含有一个艺术WaitOne(int
timeout),它好判委托是否好工作,在工作不就前主线程可以延续其他干活。运行下面代码可得同下
IAsyncResult.IsCompleted 同样的结果,而且再也简便方便 。

Ajax 32

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 33

当要监视多个运行目标的当儿,使用IAsyncResult.WaitHandle.WaitOne可尽管派不达到用场了。
幸好.NET也WaitHandle准备了另外两单静态方法:WaitAny(waitHandle[],
int)与WaitAll (waitHandle[] , int)。
个中WaitAll在等所有waitHandle完成后再也回去一个bool值。
而WaitAny是等中一个waitHandle完成后即便返回一个int,这个int是意味着已经成功waitHandle在waitHandle[]惨遭之数组索引。
脚就是以WaitAll的事例,运行结果跟以 IAsyncResult.IsCompleted
相同。

Ajax 34

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 35

   

4.7 回调函数

运轮询方式来检测异步方法的状态大辛苦,而且效率不强,有呈现与这个,.NET为
IAsyncResult BeginInvoke(AsyncCallback ,
object)准备了一个回调函数。使用 AsyncCallback
就得绑定一个方法作为回调函数,回调函数必须是带来参数 IAsyncResult
且不论返回值的方式: void AsycnCallbackMethod(IAsyncResult result)
。在BeginInvoke方法成功后,系统就是会见调用AsyncCallback所绑定的回调函数,最后回调函数中调用
XXX EndInvoke(IAsyncResult result)
就好了异步方法,它的回来值类型与信托的返回值一致。

Ajax 36

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 37

可以看,主线在调用BeginInvoke方法好继续执行其他命令,而任由需还等了,这确比采用轮询方式判断异步方法是否做到还发生优势。
当异步方法执行得后拿会调用AsyncCallback所绑定的回调函数,注意一点,回调函数依然是在异步线程中实施,这样即便非会见影响主线程的运作,这为使回调函数最值得青昧的地方。
于回调函数中发出一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就可由此
AsyncResult.AsyncDelegate
获取原委托,再运EndInvoke方法获得计算结果。
运转结果如下:

Ajax 38

假若想也回调函数传送一些表面信息,就足以以BeginInvoke(AsyncCallback,object)的末尾一个参数object,它同意外部为回调函数输入外类型的参数。只待以回调函数中采取
AsyncResult.AsyncState 就可以赢得object对象。

Ajax 39

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 40

运行结果:

Ajax 41

   

相关文章