CLR为每个进程维护了一个线程池,初始时它是空的 。但当一个线程被创建且被进程使用之后,并且完成了它的执行时 ,它并不被
销毁,而是加入到进程的线程池中。之后,当进程再次需要线程时,它会重新利用池中的线程,这样节省了大量的时间。
线程的复杂性:
尽管多线程的概念很简单,但使所有的细节都正确是比较困难的,以下需要被考虑:
1〉线程间的通讯, 线程间的通讯仅有很少的内建机制,所以使用内存是最简单的机制,因为内存对所有同一进程内的所有线程可见
并可被访问。
2〉线程的协调,线程的创建很简单,但仍需协调它们的行为。比如一个线程需要等待一个或更多的其他线程完成执行后才可继续自
身的执行。
3〉同步资源的使用, 因为同一进程内的所有线程共享进程的内存和资源,必须保证不同的线程不可以同时访问和改变它们,避免资
源/内存状态不一致。
当创建复杂的多线程系统时可以使用System.Threading命名空间下的类和类型,包括Thread类本身和其他的如: Mutex(互斥
锁),Semaphore(信号量),Monitor(监视器).它们用于资源的同步使用.这些概念都是比较底层且难懂的,最好找些操作系统原理方面的
资料参考.
你可往你的程序中加入强大的多线程功能通过两个简单的技术: 异步委托和定时器(asynchronous delegates and timers),对于大
部分程序这就是你可能唯一需要的线程技术。
C# 有个非常易用的使方法异步执行的机制:delegates(委托)。委托对象都有一个调用列表,当列表中 仅有一个方法时
,它可被异步执行;委托类的BeginInvoke和EndInvoke方法即为此目的。使用步骤如下:
1> 当调用委托对象的BeginInvoke() 方法时,它开始在一个来自线程池的线程中执行委托指向的那个方法,之后立即返回到初始的线
程,起初的那个线程将继续执行下面的代码.同时被委托执行的那个线程也在并行执行着.
2> 当你的程序想提取异步执行的方法的结果时,可先检查由BeginInvoke()方法返回的IAsyncResult的IsCompleted属性,或调用
EndInvoke()方法等待委托执行完成.
三种标准的使用模式:
1>wait-until-done等待直到完成模式,在产生一个异步方法的执行后(Beginnvoke()),初始线程做一些其他的处理,之后会挂起并等待
异步方法的结束,以便继续执行.不同于同步方法的是同步方法一旦被调用便初始线程便不能做任何其他的处理---控制完全转移到被
调用的方法那里去了.
2>Polling轮询模式,初始线程周期性的检查新生的线程是否完成了执行,没有的话继续其他的处理.
3>callback回调模式,初始线程不会等待或检查卵生线程是否完成了执行,而是:当被委托引用的方法在卵生(spawned)线程中执行完成
时,子线程会调用一个回调方法,该方法在调用EndInvoke()之前处理异步方法的调用结果.
注意:原始线程可称为父线程,由其产生(spawn)的线程可称为子线程.
对于BeginInvoke()有一些信息必须知道:
1>当调用BeginInvoke时参数列表含以下的实际参数:
a:被委托引用的函数所需要的参数b: 另外的两个参数: callback和state参数;
2>BeginInvoke()从线程池中取出一个线程并启动引用的方法在新 线程中运行.
3>BeginInvoke()返还给调用线程一个实现了IAsyncResult接口 的 对象 。此接口的引用含有当前执行的异步方法的执行状态信息。
初始线程之后继续执行。
考虑现在很被人鼓吹的Ajax也只不过如这里的思想.
EndInvoke()方法被用于提取由异步执行的方法调用的返回值.且被线程用于释放资源。EndInvoke 有以下特性:
1>它会用一个 指向IAsyncResult的引用作为参数,IAsyncResult就是BeginInvoke的返回 值,并找到它(IAsyncResult)引用的线
程。
2>如果线程池中那个线程已经退出,EndInvoke作如下的事情:
a:清理已退出的线程释放其所占资源。b:找到相应方法的返回值并 将其作为自己的返回值。
如果在EndInvoke调用时,线程池中的那个线程依旧在运行,调用者线程(父线程)会停止并等待它直到完成清理和返回值。因为
EndInvoke会对产生的线程做清理工作,所以必须保证针对每一个BeginInvoke()都调用了EndInvoke()(如果你不想资源泄露的话);
3>如果异步方法触发了一个异常,当调用EndInvoke()时异常会被激发.
EndInvoke提供所有源自异步方法的输出,包括ref和out参数.如果委托的相应方法有ref和out参数,它们必须在EndInvoke()方法的参
数中出现,且先于IAsyncResult的位置:delegate.EndInvoke(out somePara, iAsyncResult);
等待直到完成模式:
namespace WaitUtilDone
{
public delegate string AsycDelegate(out string outArg, string inArg);//委托定义
class Program
{
static void Main(string[] args)
{
AsycDelegate myAsycDelegate = (AsycDelegate)AsyncWorker.AsyncMethod;//定义一个委托成员变量,这里用的是简
洁形式
string fromAsyncMethrod;
IAsyncResult iAsyncRsl= myAsycDelegate.BeginInvoke(out fromAsyncMethrod, "toAsycMethod", null, null);
//主线程中........可在此段时间做其他的事
Thread.Sleep(2000);//主线程休息2秒钟
string returnStrFromAsyncMethod = myAsycDelegate.EndInvoke(out fromAsyncMethrod,iAsyncRsl );
System.Console.WriteLine("the string return from AsyncMethod is {0}",returnStrFromAsyncMethod );
System.Console.WriteLine("the fromAsyncMethod is :{0}",fromAsyncMethrod);
System.Console.ReadLine();
}
}
class AsyncWorker {
public static string AsyncMethod(out string fromThisMethod,string toThisMethod) {
System.Console.WriteLine("<< enter the AsyncMethod!");
fromThisMethod = "string form AsyncWork.AsyncMehod as a out parameter";
System.Console.WriteLine("传入此异步方法的参数:{0}",toThisMethod );
Thread.Sleep(6000);//该方法会阻塞运行此方法的线程六秒
System.Console.WriteLine("end the AsyncMehod >>");
return "returnValueFromAsyncMethod!";
}
}
}
!关于IAsyncResult:它是BeginInvoke()和EndInvoke()不可或缺的部分.BeginInvoke方法会返回一个AsyncResult类型的对象引用,该
类AsyncResult实现了ISyncResult接口,AsyncReslut表示异步方法的状态,对该类需要知道以下重要特征:
1>当调用委托对象的BeginInvoke方法时系统创建一个该类的实例,将此对象的引用返回给ISyncResult 接口.
2> AsyncResult对象 含有一AsyncDelegate的属性,它返回一个指向调用异步方法的委托的引用,该属性是AsyncResult 的属性而不是
IAyncResult接口的属性.
3>IsCompleted属性返回一个布尔值,表示异步方法是否被执行完毕.
4>AsyncState属性返回一个作对象的引用,它会作为BeginInvoke方法的调用参数返回的引用是个Object类型.回调模式会用到它.
@@轮询模式:
轮询模式中,原线程初始一个异步方法的调用后,做一些额外的其他工作,然后周期性的检查 IAsyncResult 所指对象的Isompleted
,看异步方法是否完成执行,如果完成,则会调用EndInvoke 并继续执行,否则做一些其他工作,并继续检查.:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace PoolingPattern
{
public delegate string AsyncDelegate();
class Program
{
static string AsycMethod() {
System.Console.WriteLine("<< enter the asyncMethod.....");
Thread.Sleep(3000);
System.Console.WriteLine("......end the asyncMethod>>");
return "returnValueFromTheAsyncMethod";
}
static void Main(string[] args)
{
AsyncDelegate myAsyncDele = new AsyncDelegate(AsycMethod );//构造一个委托
System.Console.WriteLine("main方法中开始 调用 异步方法");
IAsyncResult iAsyncRslt = myAsyncDele.BeginInvoke(null,null);
while (!iAsyncRslt .IsCompleted ){
System.Console.WriteLine("主线程中检查->:..异步调用的方法仍没有 完成...");
for (long i = 0; i <= 100000000;i++ )
;//做一些 自己的事,这里是 空耗 cpu
}//轮询 结束 时异步方法应已经完成了
/*此时可提起异步执行的结果了*/
string strFromAsyncMethod = myAsyncDele.EndInvoke(iAsyncRslt);
System.Console.WriteLine("异步方法返回的结果:{0}",strFromAsyncMethod );
System.Console.WriteLine("main方法结束");
System.Console.ReadLine();
}
}
}
##前两个模式中,原始线程(运行main()方法的线程)仅在卵生线程完成(等待或轮询)时,提取异步方法的结果并继续主线程的
运行。回调模式不同它们:父线程异步调用委托后并不需等待或轮询,当异步方法完成执行时系统会调用一个回调方法去处理异步方
法的结果,并调用EndInvoke方法释放相关资源。BeginInvoke方法的最后两个参数就是用与回调目的:
BeginInvoke后两个参数的第一个参数就是回调函数的名称(函数名称可转化为委托!!)。第二个参数state可是null或想传给回调
函数的一个对象的引用,可通过方法的IAsyncResult参数(用其AsyncState属性 )访问该对象。参数的类型是object型使用时需显
式转型为特定的型别。
@@回调函数的签名必须与AsyncCallback委托的形式一致,此形式要求回调函数需要一个IAsyncResult类型的参数和空的返回值。
void AsyncCallback( IAsyncResult iar );
有多种将回调函数提供给BeginInvoke方法的途径。因为回调参数在BeginInvoke中是类型为AsyncCallback委托类型,你可以直接提
供一个函数名,或是先构造一个委托对象。记住特定格式的函数的函数名总是可被编译器隐式转换为一个相应的委托。以下为我写的
小练习:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace CallBackPattern
{
class Program
{
static void callBackMethod(IAsyncResult asyncRslt) {
System.Console.WriteLine("<<...Enter the CallbackMethod");
System.Console.WriteLine("此回调函数在异步方法执行结束后调用的");
AsyncResult myAsynResult = (AsyncResult)asyncRslt;
EventHandler asyncDele = (EventHandler)myAsynResult.AsyncDelegate;
asyncDele.EndInvoke(asyncRslt);//资源回收见下面解释
System.Console.WriteLine("End the CallbackMethod..>>");
}
static void myAsyncMethod (object source,System .EventArgs evtArgs){
System.Console.WriteLine("《《进入异步方法调用区域");
Thread.Sleep(5000);
System.Console.WriteLine("结束异步方法的调用区域》》");
}
static void Main(string[] args)
{
System.Console.WriteLine("进入主线程....");
EventHandler myAsyncDelegate = (EventHandler)myAsyncMethod;/*偷懒了,用的是系统提供的委托,注意方法名可
转换为一个委托*/
IAsyncResult iAsyncRslt= myAsyncDelegate.BeginInvoke(null,null,callBackMethod ,null);
//使用隐式转换等价下面的方式
//AsyncCallback myCallback = new AsyncCallback(callBackMethod);
// IAsyncResult iAsyncRslt= myAsyncDelegate.BeginInvoke(null,null,callBackMethod ,null);
System.Console.WriteLine("主线程结束,等待回调方法被调用.....");
System.Console.ReadLine();
}
}
}
**在回调方法中应该调用EndInvoke方法。且应该处理异步方法的返回值和out参数(如果有的话!)为了调用异步方法的EndInvoke
方法你需要有一个指向异步方法的委托引用,它在初始线程中,而不在卵生线程中,如果没有使用BeginInvoke的state参数做任何事
,你就可用其传递委托的引用给回调方法:IAsyncResult iar = delegate.BeginInvoke(args,CallWhenDone, delegate);
在回调函数中可通过IAsyncResult类型的参数获取委托的引用。注意:IAsyncResult接口实际指向的是AsyncResult类的实例。尽管
接口不含委托引用但AsyncResult类的对象持有委托的引用。AsyncResult类位于System.Runtime.Remoting.Messaging命名空间。当
通过AsyncResult实例的AsyncDelegate属性获取委托并将之进行合适转型。此后便可调用EndInvoke方法。
using System.Runtime.Remoting.Messaging; // 包含 AsyncResult类
void CallWhenDone( IAsyncResult iar )
{
AsyncResult ar = (AsyncResult) iar; //将接口转型为AsyncResult实例
MyDel del = (MyDel) ar.AsyncDelegate; //获取委托并转型为适当的委托
long Sum = del.EndInvoke( iar ); // 调用EndInvoke
...
}