线程:定义为可执行应用程序中的基本执行单元。
应用程序域:一个应用程序内可能有多个线程。
上下文:一个线程可以移动到一个特定的上下文的实体
导入命名空间:
//得到正在执行这个方法的线程 Thread currThread = Thread.CurrentThread; //获取正在承载当前线程的应用程序 AppDomain ad = Thread.GetDomain(); //获取当前操作线程所处的上下文 System.Runtime.Remoting.Contexts.Context ct = Thread.CurrentContext;
线程同步的作用:
同一时间,多个线程都能运行共享的功能块。为了保护资源不被破坏,使用各种原语(比如lock,monitor和[Synchronization]特性或语言关键字支持)来控制线程对它们的访问。
使用System.Threading命名空间中定义的类型,.NET4.0或更高版本的TPL(Task Parallel Library,任务并行库)以及.NET4.5中C#的async和await语言关键字,可以比较放心的操作线程。
想要更好的了解线程的方法,先得知道.NET的委托。
委托
.net委托是一个类型安全的、面向对象的函数指针。当定义了一个.NET委托类型时,作为响应,C#编译器将创建一个派生自System.MulticastDelegate的密封类(派生于System.Delegate) (关于委托的更多介绍以后会写到)
public delegate int BinaryOp(int x,int y); //委托跟类同级 //它能指向任何一个拥有两个整数参数、返回一个整数的方法
例如:
public delegate int BinaryOp(int x,int y); //委托跟类同级 //它能指向任何一个拥有两个整数参数、返回一个整数的方法 //public sealed class BinaryOp : MulticastDelegate //{ // public BinaryOp(object target, uint functionAddress); // public int Invoke(int x,int y); // public IAsyncResult BeginInvoke(int x,int y,AsyncCallback cb,object state); // public int EndInvoke(IAsyncResult result); //} class Program { static void Main(string[] args) { Console.WriteLine("****请开始你的表演***"); //输出正在执行中的线程ID Console.WriteLine("Main() invoked on thread {0}",Thread.CurrentThread.ManagedThreadId); //在同步模式下调用Add() BinaryOp b = new BinaryOp(Add);//指向了Add这个方法 //也能写 b.Invoke(10,10); int answer = b(10, 10); //直到Add()方法完了后,在会继续执行下面的 Console.WriteLine("Doing more work in Main()!"); Console.WriteLine("10+10 is {0}.",answer); Console.WriteLine(); } static int Add(int x, int y) { //输出正在执行中的线程ID Console.WriteLine("Add() invoked onthread {0}",Thread.CurrentThread.ManagedThreadId); //暂定一下,模拟一个耗时的操作 Thread.Sleep(5000); return x + y; } }
结果:
因为是同步模式下调用了Add方法,由于程序中所有的任务都是被主线程执行,所有显示的ID是相同的值
BeginInvoke()和EndInvoke()
C#编译器处理delegate关键字的时候,其动态生成的类定义了两个方法BeginInvoke和EndInvoke
public IAsyncResult BeginInvoke(int x,int y,AsyncCallback cb,object state); //用于异步调用的方法 public int EndInvoke(IAsyncResult result); //用于获取被调用方法的返回值
先来看看BeginInvoke里面的参数。
传入BeginInvoke()的参数的最初集合必须符合C#委托约定(对于BinaryOp,就是两个整型,后两个参数不知道可以给空)。EndInvoke()唯一参数总是IAsyncResult类型。
System.IAsyncResult 接口
查看定义
// 摘要: // 表示异步操作的状态。 [ComVisible(true)] public interface IAsyncResult { // 摘要: // 获取用户定义的对象,它限定或包含关于异步操作的信息。 // // 返回结果: // 用户定义的对象,它限定或包含关于异步操作的信息。 object AsyncState { get; } // // 摘要: // 获取用于等待异步操作完成的 System.Threading.WaitHandle。 // // 返回结果: // 用于等待异步操作完成的 System.Threading.WaitHandle。 WaitHandle AsyncWaitHandle { get; } // // 摘要: // 获取一个值,该值指示异步操作是否同步完成。 // // 返回结果: // 如果异步操作同步完成,则为 true;否则为 false。 bool CompletedSynchronously { get; } // // 摘要: // 获取一个值,该值指示异步操作是否已完成。 // // 返回结果: // 如果操作完成则为 true,否则为 false。 bool IsCompleted { get; }
BeginInvoke()返回的对象实现了IAsyncResult接口,而EndInvoke()需要一个IAsyncResult兼容类型作为它的参数。
所有如果异步调用一个无返回值的方法就不需要EndInvoke方法了。
异步调用方法:
还是上面的列子,修改一下
static void Main(string[] args) { Console.WriteLine("****请开始你的表演***"); //输出正在执行中的线程ID Console.WriteLine("Main() invoked on thread {0}",Thread.CurrentThread.ManagedThreadId); //在次线程中调用add BinaryOp b = new BinaryOp(Add);//指向了Add这个方法 IAsyncResult ifAR = b.BeginInvoke(10, 10, null, null); Console.WriteLine("Doing more work in Main()!"); //执行完后获取Add()方法的结果 int answer = b.EndInvoke(ifAR); Console.WriteLine("10+10 is {0}.",answer); Console.WriteLine(); }
我们看到了两个不同的ID值,这说明在这个应用程序域中有两个线程正在运行。
同步调用线程
我们一运行,Doing more work inMian() 这个语句就会出现,并没有等到5秒。这样的异步调用中主线程可能会被堵塞,那我们的做异步的优势就不明显了,效率似乎不高,我们需要做另一个同步调用:
IAsyncResult提供给了IsCompleted属性。使用这个成员,调用线程在调用EndInvoke()之前,便能判断异步调用是否真正完成。
如果方法没有完成,IsCompleted方法false。
继续改我们的代码:
static void Main(string[] args) { Console.WriteLine("****请开始你的表演***"); //输出正在执行中的线程ID Console.WriteLine("Main() invoked on thread {0}",Thread.CurrentThread.ManagedThreadId); //在同步模式下调用Add() BinaryOp b = new BinaryOp(Add);//指向了Add这个方法 IAsyncResult ifAR = b.BeginInvoke(10, 10, null, null); //在Add()方法完成之前会一直显示消息 while (!ifAR.IsCompleted) { Console.WriteLine("Doing more work in Main()!"); Thread.Sleep(1000); } // Console.WriteLine("Doing more work in Main()!"); //我们指定Add()方法调用完成了 int answer = b.EndInvoke(ifAR); //直到Add()方法完了后,在会继续执行下面的 Console.WriteLine("10+10 is {0}.",answer); Console.WriteLine(); }
首先我们会执行BeginInvoke方法,会去执行Add()方法,由于需要5秒完成,所以会输出5此doing,我们打断点可以看出先执行Add()在输出doing,直接运行Doing会先出现一遍。
除了IsCompleted属性之外,IAsyncResult接口还提供了AsyncWaiHandle属性以实现更好的灵活的的等待逻辑。返回WaitHandle类型的实例,改实例公开了一个WaitOne()的方法。
此方法可以指定最长等待时间如果超时,返回false.
while (!ifAR.AsyncWaitHandle.WaitOne(1000,true)) //没有完成异步会一直执行 { Console.WriteLine("Doing more work in Main()!"); }
这种的形式比上面的好理解些。
AsyncCallback 委托的作用