首页 > 代码库 > System.Threading

System.Threading

 

线程:定义为可执行应用程序中的基本执行单元。

应用程序域:一个应用程序内可能有多个线程。

上下文:一个线程可以移动到一个特定的上下文的实体

导入命名空间:

技术分享

 

          //得到正在执行这个方法的线程
            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#的asyncawait语言关键字,可以比较放心的操作线程。

想要更好的了解线程的方法,先得知道.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 委托的作用

 

System.Threading