C# 多线程编程第一步——理解多线程

1、进程、线程及多线程的概念html

什么是多线程呢?不理解。编程

那什么是线程呢?说到线程就不得不说说进程。我在网上搜索也搜索了一些资料,大部分所说的进程实际上是很抽象的东西。通俗的来说,进程就是一个应用程序开始运行,那么这个应用程序就会存在一个属于这个应用程序的进程。安全

那么线程就是进程中的基本执行单元,每一个进程中都至少存在着一个线程,这个线程是根据进程建立而建立的,因此这个线程咱们称之为主线程。那么多线程就是包含有除了主线程以外的其余线程。若是一个线程能够执行一个任务,那么多线程就是能够同时执行多个任务。多线程

以上的概念纯属我的理解,若有什么不对的地方,还请多多指正。异步

 

2、线程的基本知识async

Thread 类函数

Thread 类是用于控制线程的基础类,它存在于 System.Threading 命名空间。经过 Thread 能够控制当前应用程序域中线程的建立、挂起、中止、销毁。性能

Thread 一些经常使用属性:this

Thread 一些经常使用方法:spa

Thread 的优先级:

 

3、多线程的简单示例

下面就从简单的多线程开始理解吧,这里我建立了一个控制台应用程序。

   class Program
    {
        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();

            //建立一个新的线程
            Thread thread = new Thread(demoClass.Run);

            //设置为后台线程
            thread.IsBackground = true;

            //开始线程
            thread.Start();

            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());

            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public void Run()
        {
            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

建立一个新的线程还可使用 ThreadStart 委托的方式。以下:

//建立一个委托,并把要执行的方法做为参数传递给这个委托
ThreadStart threadStart = new ThreadStart(demoClass.Run);
Thread thread = new Thread(threadStart);

执行结果:

根据以上的结果咱们能够分析获得,主线程建立了一个子线程并启动了它,可是主线程没有等到子线程执行完成,而是继续再往下执行的。

这就涉及到了线程异步或同步的问题了,这个咱们后面再说。

继续上面的问题,那么若是我想要等到子线程执行完成以后再继续主线程的工做呢(固然,我以为通常不会有这种需求)。

咱们可使用 Join() 这个方法,修改以后的代码:

   class Program
    {
        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();

            //建立一个新的线程
            Thread thread = new Thread(demoClass.Run);

            //设置为后台线程
            thread.IsBackground = true;

            //开始线程
            thread.Start();

            //等待直到线程完成
            thread.Join();

            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());

            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public void Run()
        {
            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

执行结果:

上面的代码相比以前就添加了一句 thread.Join(),它的做用就是用于阻塞后面的线程,直到当前线程完成以后。固然,还有其余的方法能够作到,好比咱们如今把 thread.Join() 换成

下面这句代码。

//挂起当前线程指定的时间
Thread.Sleep(100);

就当前的场景来讲,这样的确能够知足需求,可是这样作有一个弊端,就是,当子线程所执行的方法逻辑比较复杂耗时较长的时候,这样的方式就不必定能够,虽然能够修改线程挂起的时间,可是这个执行的时间倒是不定的。因此,Thread.Sleep() 方法通常用来设置多线程之间执行的间隔时间的。

另外,Join() 方法也接受一个参数,该参数用于指定阻塞线程的时间,若是在指定的时间内该线程没有终止,那么就返回 false,若是在指定的时间内已终止,那么就返回 true。

 

上面的这种使用多线程的方式只是简单的输出一段内容而已,多数状况下咱们须要对线程调用的方法传入参数和接收返回值的,可是上面这种方法是不接受参数而且没有返回值的,那么咱们可使用 ParameterizedThreadStart 委托来建立多线程,这个委托能够接受一个 object 类型的参数,咱们能够在这上面作文章。

   class Program
    {static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();

            //建立一个委托,并把要执行的方法做为参数传递给这个委托
            ParameterizedThreadStart threadStart = new ParameterizedThreadStart(demoClass.Run);
            
            //建立一个新的线程
            Thread thread = new Thread(threadStart);

            //开始线程,并传入参数
            thread.Start("Brambling");

            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public void Run(object obj)
        {
            string name = obj as string;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("My name is " + name);
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

执行结果:

PS:这里我没有加这句代码了(thread.IsBackground = true,即把当前线程设置为后台线程),由于使用 thread.Start() 启动的线程默认为前台线程。那么前台线程和后台线程有什么区别呢?

前台线程就是系统会等待全部的前台线程运行结束后,应用程序域才会自动卸载。而设置为后台线程以后,应用程序域会在主线程执行完成时被卸载,而不会等待异步线程的执行完成。

那么上面的结果能够看到在多线程实现了参数的传递,但是它也只有一个参数呢。可是它接受的参数是 object 类型的(万类之源),也就是说既能够是值类型或引用类型,也能够是自定义类型。(固然,自定义类型其实也是属于引用类型的)下面咱们使用自定义类型做为参数传递。

   class Program
    {
        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();

            //建立一个委托,并把要执行的方法做为参数传递给这个委托
            ParameterizedThreadStart threadStart = new ParameterizedThreadStart(demoClass.Run);

            //建立一个新的线程
            Thread thread = new Thread(threadStart);

            UserInfo userInfo = new UserInfo();
            userInfo.Name = "Brambling";
            userInfo.Age = 333;

            //开始线程,并传入参数
            thread.Start(userInfo);

            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public void Run(object obj)
        {
            UserInfo userInfo = (UserInfo)obj;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("My name is " + userInfo.Name);
            Console.WriteLine("I'm " + userInfo.Age + " years old this year");
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

    public class UserInfo
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }

执行结果:

使用自定义类型做为参数传递,理论上更多个参数也都是能够实现的。

 

4、线程池

使用 ThreadStart 和 ParameterizedThreadStart 建立线程仍是比较简单的,可是因为线程的建立和销毁须要耗费必定的开销,过多的使用线程反而会形成内存资源的浪费,从而影响性能,出于对性能的考虑,因而引入了线程池的概念。线程池并非在 CLR 初始化的时候马上建立线程的,而是在应用程序要建立线程来执行任务的时候,线程池才会初始化一个线程,初始化的线程和其余线程同样,可是在线程完成任务以后不会自行销毁,而是以挂起的状态回到线程池。当应用程序再次向现成池发出请求的时候,线程池里挂起的线程会再度激活执行任务。这样作能够减小线程建立和销毁所带来的开销。线程池创建的线程默认为后台线程

   class Program
    {
        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();

            //设置当没有请求时线程池维护的空闲线程数
            //第一个参数为辅助线程数
            //第二个参数为异步 I/O 线程数
            ThreadPool.SetMinThreads(5, 5);

            //设置同时处于活动状态的线程池的线程数,全部大于次数目的请求将保持排队状态,直到线程池变为可用
            //第一个参数为辅助线程数
            //第二个参数为异步 I/O 线程数
            ThreadPool.SetMaxThreads(100, 100);

            //使用委托绑定线程池要执行的方法(无参数)
            WaitCallback waitCallback1 = new WaitCallback(demoClass.Run1);
            //将方法排入队列,在线程池变为可用时执行
            ThreadPool.QueueUserWorkItem(waitCallback1);


            //使用委托绑定线程池要执行的方法(有参数)
            WaitCallback waitCallback2 = new WaitCallback(demoClass.Run1);
            //将方法排入队列,在线程池变为可用时执行
            ThreadPool.QueueUserWorkItem(waitCallback2,"Brambling");


            UserInfo userInfo = new UserInfo();
            userInfo.Name = "Brambling";
            userInfo.Age = 33;

            //使用委托绑定线程池要执行的方法(有参数,自定义类型的参数)
            WaitCallback waitCallback3 = new WaitCallback(demoClass.Run2);
            //将方法排入队列,在线程池变为可用时执行
            ThreadPool.QueueUserWorkItem(waitCallback3, userInfo);

            Console.WriteLine();
            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public void Run1(object obj)
        {
            string name = obj as string;

            Console.WriteLine();
            Console.WriteLine("Child thread working...");
            Console.WriteLine("My name is " + name);
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }

        public void Run2(object obj)
        {
            UserInfo userInfo=(UserInfo)obj;

            Console.WriteLine();
            Console.WriteLine("Child thread working...");
            Console.WriteLine("My name is " + userInfo.Name);
            Console.WriteLine("I'm " + userInfo.Age + " years old this year");
            Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
    }

    public class UserInfo
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }

执行结果:

使用线程池创建的线程也能够选择传递参数或不传递参数,而且参数也能够是值类型或引用类型(包括自定义类型)。看上面的结果发现了什么?没错,第一次执行的方法的线程ID为6,最后一次执行的方法的线程ID也为6。这就说明第一次请求线程池的时候,线程池创建了一个线程,当它执行完成以后就以挂起状态回到了线程池,在最后一次请求的时候,再次唤醒了该线程执行任务。这样就很容易理解了。

在这里我还发现了一个问题,就是,每次运行的时候,输出的内容的顺序都不必定是同样的。(不仅是线程池,前面的也是)由于,我没有给任何线程设置优先级(线程池不能设置线程的优先级),这里其实就涉及到线程安全的问题了,很明显如今这样是非线程安全的。让我举个栗子形容一下的话,就像之前在学校下课了去吃饭同样,蜂拥而上,毫无秩序。

线程安全就先不说,留在后面再说(包括前面所提到的线程同步的问题),这篇博客旨在理解多线程。由于我也没有太深的理解。。。

上面咱们已经实现了无参数和有参数以及自定义参数多线程的实例,但是还有一个共同的问题那就是都没有返回值。当咱们用多线程作实际开发的时候大部分都是会须要返回值的,那么咱们可使用成员变量来试试。

   class Program
    {
        List<UserInfo> userInfoList = new List<UserInfo>();

        static void Main(string[] args)
        {
            Program program = new Program();

            ParameterizedThreadStart threadStart = new ParameterizedThreadStart(program.Run);
            Thread thread = null;
            UserInfo userInfo = null;


            for (int i = 0; i < 3; i++)
            {
                userInfo = new UserInfo();
                userInfo.Name = "Brambling" + i.ToString();
                userInfo.Age = 33 + i;

                thread = new Thread(threadStart);
                thread.Start(userInfo);
                thread.Join();
            }

            foreach (UserInfo user in program.userInfoList)
            {
                Console.WriteLine("My name is " + user.Name);
                Console.WriteLine("I'm " + user.Age + " years old this year");
                Console.WriteLine("Thread ID is:" + user.ThreadId);
            }
            
            Console.ReadKey();
        }

        public void Run(object obj)
        {
            UserInfo userInfo = (UserInfo)obj;

            userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId;
            userInfoList.Add(userInfo);
        }
    }

执行结果:

用上面这种方法勉强能够知足返回值的需求,可是却有很大的局限性,由于这里我使用的是成员变量,因此也就限制了线程调用的方法必须是在同一个类里面。

因此也就有了下面的方法,使用委托异步调用的方法。

 

5、异步委托

委托的异步调用有两个比较重要的方法:BeginInvoke()EndInvoke()

   class Program
    {
        //定义一个委托类
        private delegate UserInfo MyDelegate(UserInfo userInfo);


        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();
            List<UserInfo> userInfoList = new List<UserInfo>();
            UserInfo userInfo = null;
            UserInfo userInfoRes = null;

            //建立一个委托并绑定方法
            MyDelegate myDelegate = new MyDelegate(demoClass.Run);

            for (int i = 0; i < 3; i++)
            {
                userInfo = new UserInfo();
                userInfo.Name = "Brambling" + i.ToString();
                userInfo.Age = 33 + i;

                //传入参数并执行异步委托
                IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null);

                //异步操做是否完成
                while (!result.IsCompleted)
                {
                    Thread.Sleep(100);

                    Console.WriteLine("Main thread working...");
                    Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
                    Console.WriteLine();
                }

                //结束异步委托,并获取返回值
                userInfoRes = myDelegate.EndInvoke(result);

                userInfoList.Add(userInfoRes);
            }

            foreach (UserInfo user in userInfoList)
            {
                Console.WriteLine("My name is " + user.Name);
                Console.WriteLine("I'm " + user.Age + " years old this year");
                Console.WriteLine("Thread ID is:" + user.ThreadId);
            }
            
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public UserInfo Run(UserInfo userInfo)
        {
            userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + userInfo.ThreadId);
            Console.WriteLine();

            return userInfo;
        }
    }

执行结果:

BeginInvoke() 方法用于异步委托的执行开始,EndInvoke() 方法用于结束异步委托,并获取异步委托执行完成后的返回值。IAsyncResult.IsCompleted 用于监视异步委托的执行状态(true / false),这里的时间是不定的,也就是说必定要等到异步委托执行完成以后,这个属性才会返回 true。若是异步委托的方法耗时较长,那么主线程会一直工做下去。

BeginInvoke() 是能够接受多个参数的,它的参数个数和参数类型取决于定义委托时的参数个数和类型,不管它有多少个参数,最后两个参数都是不变的,下面咱们会说到。

 

那么咱们还能够用下面的方法 WaitOne(),自定义一个等待的时间,若是在这个等待时间内异步委托没有执行完成,那么就会执行 while 里面的主线程的逻辑,反之就不会执行。

   class Program
    {
        //定义一个委托类
        private delegate UserInfo MyDelegate(UserInfo userInfo);


        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();
            List<UserInfo> userInfoList = new List<UserInfo>();
            UserInfo userInfo = null;
            UserInfo userInfoRes = null;

            //建立一个委托并绑定方法
            MyDelegate myDelegate = new MyDelegate(demoClass.Run);

            for (int i = 0; i < 3; i++)
            {
                userInfo = new UserInfo();
                userInfo.Name = "Brambling" + i.ToString();
                userInfo.Age = 33 + i;

                //传入参数并执行异步委托
                IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null);

                //阻止当前线程,直到 WaitHandle 收到信号,参数为指定等待的毫秒数
                while (!result.AsyncWaitHandle.WaitOne(1000))
                {
                    Console.WriteLine("Main thread working...");
                    Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
                    Console.WriteLine();
                }

                //结束异步委托,并获取返回值
                userInfoRes = myDelegate.EndInvoke(result);

                userInfoList.Add(userInfoRes);
            }

            foreach (UserInfo user in userInfoList)
            {
                Console.WriteLine("My name is " + user.Name);
                Console.WriteLine("I'm " + user.Age + " years old this year");
                Console.WriteLine("Thread ID is:" + user.ThreadId);
            }
            
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public UserInfo Run(UserInfo userInfo)
        {
            userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + userInfo.ThreadId);
            Console.WriteLine();

            return userInfo;
        }
    }

执行结果:

WaitOne() 方法只能用于监视当前线程的对象,若是要监视多个对象可使用 WaitAny(WaitHandle[], int)WaitAll (WaitHandle[] , int) 这两个方法。

   class Program
    {
        //定义一个委托类
        private delegate UserInfo MyDelegate(UserInfo userInfo);

        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();
            List<UserInfo> userInfoList = new List<UserInfo>();
            UserInfo userInfo = null;
            UserInfo userInfoRes = null;

            //建立一个委托并绑定方法
            MyDelegate myDelegate = new MyDelegate(demoClass.Run);

            for (int i = 0; i < 3; i++)
            {
                userInfo = new UserInfo();
                userInfo.Name = "Brambling" + i.ToString();
                userInfo.Age = 33 + i;

                //传入参数并执行异步委托
                IAsyncResult result = myDelegate.BeginInvoke(userInfo,null,null);
                IAsyncResult result1 = myDelegate.BeginInvoke(userInfo, null, null);

                //定义要监视的对象,不能包含对同一对象的多个引用
                WaitHandle[] waitHandles = new WaitHandle[] { result.AsyncWaitHandle, result1.AsyncWaitHandle };
                while (!WaitHandle.WaitAll(waitHandles,1000))
                {
                    Console.WriteLine("Main thread working...");
                    Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
                    Console.WriteLine();
                }

                //结束异步委托,并获取返回值
                userInfoRes = myDelegate.EndInvoke(result);
                userInfoList.Add(userInfoRes);

                userInfoRes = myDelegate.EndInvoke(result1);
                userInfoList.Add(userInfoRes);
            }

            foreach (UserInfo user in userInfoList)
            {
                Console.WriteLine("My name is " + user.Name);
                Console.WriteLine("I'm " + user.Age + " years old this year");
                Console.WriteLine("Thread ID is:" + user.ThreadId);
            }
            
            Console.ReadKey();
        }
    }

    public class ThreadDemoClass
    {
        public UserInfo Run(UserInfo userInfo)
        {
            userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + userInfo.ThreadId);
            Console.WriteLine();

            return userInfo;
        }
    }

执行结果:

WaitAll() 方法和 WaitAny() 方法均可以监视多个对象,不一样的是 WaitAll() 方法须要等待全部的监视对象都收到信号以后才会返回 true,不然返回 false。而 WaitAny() 则是当有一个监视对象收到信号以后就会返回一个 int 值,这个 int 值表明的是当前收到信号的监视对象的索引。注意:在定义监视对象的时候,不能包含对同一个对象的多个引用,我这里是定义的两个示例,因此是不一样的对象。

接下来咱们看上面的执行结果。咦?为何主线程没有“工做”呢?

这里你能够在异步委托调用的 Run() 方法里面为线程设置一个几秒钟或者更长的挂起时间。而后在设置监视对象这里设置一个小于线程挂起的时间,而后调试你就能发现问题的所在了。其实也不算是问题,只是以前的理解有误。

其实 WaitAll() 方法和 WaitAny() 方法设置监视对象,而后指定一个时间(毫秒值),这里的意思是当全部的监视对象在指定的时间内都接收到信号时(这里是指 WaitAll() 方法),就不会执行 while 里面的主线程的工做,反之就会执行。

这里你可能会有疑问,若是是这样,那我把前面的逻辑非运算符去掉那不就相反了么。这么理解逻辑上是没错的,可是我仍是要说的是,尽可能不要去尝试,由于这会是个死循环。WaitAny() 这个方法也是同样的理解,不一样的是,它不须要等到全部的监视对象都收到信号,它只须要一个监视对象收到信号就够了,这里就不在演示了。

 

上面的方法能够看出,咱们虽然使用的是异步的方式调用的方法,可是依旧须要等待异步的方法返回执行的结果,尽管咱们能够不阻塞主线程,可是仍是以为不太方便。因此也就有了本篇博客最后一个要点,异步委托的回调函数

   class Program
    {
        //定义一个委托类
        private delegate UserInfo MyDelegate(UserInfo userInfo);
static void Main(string[] args) { ThreadDemoClass demoClass = new ThreadDemoClass(); UserInfo userInfo = null; //建立一个委托并绑定方法 MyDelegate myDelegate = new MyDelegate(demoClass.Run); //建立一个回调函数的委托 AsyncCallback asyncCallback = new AsyncCallback(Complete); for (int i = 0; i < 3; i++) { userInfo = new UserInfo(); userInfo.Name = "Brambling" + i.ToString(); userInfo.Age = 33 + i; //传入参数并执行异步委托,并设置回调函数 IAsyncResult result = myDelegate.BeginInvoke(userInfo, asyncCallback, null); } Console.WriteLine("Main thread working..."); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine(); Console.ReadKey(); } public static void Complete(IAsyncResult result) { UserInfo userInfoRes = null; AsyncResult asyncResult = (AsyncResult)result; //获取在其上调用异步调用的委托对象 MyDelegate myDelegate = (MyDelegate)asyncResult.AsyncDelegate; //结束在其上调用的异步委托,并获取返回值 userInfoRes = myDelegate.EndInvoke(result); Console.WriteLine("My name is " + userInfoRes.Name); Console.WriteLine("I'm " + userInfoRes.Age + " years old this year"); Console.WriteLine("Thread ID is:" + userInfoRes.ThreadId); } } public class ThreadDemoClass { public UserInfo Run(UserInfo userInfo) { userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Child thread working..."); Console.WriteLine("Child thread ID is:" + userInfo.ThreadId); Console.WriteLine(); return userInfo; } }

执行结果:

从上面能够看到主线程再执行了异步委托以后继续执行了下去,而后在回调函数里输出了信息,也就是说在调用了异步委托以后就无论了,把以后的结束委托和获取委托的返回值放到了回调函数中,由于回调函数是没有返回值的,可是回调函数能够有一个参数。上面说到的 BeginInvoke() 方法的最后两个参数,它的倒数第二个参数就是一个回调函数的委托,最后一个参数能够设置传入回调函数的参数。以下:

   class Program
    {
        //定义一个委托类
        private delegate UserInfo MyDelegate(UserInfo userInfo);

        static List<UserInfo> userInfoList = new List<UserInfo>();

        static void Main(string[] args)
        {
            ThreadDemoClass demoClass = new ThreadDemoClass();
            UserInfo userInfo = null;

            //建立一个委托并绑定方法
            MyDelegate myDelegate = new MyDelegate(demoClass.Run);

            //建立一个回调函数的委托
            AsyncCallback asyncCallback = new AsyncCallback(Complete);

            //回调函数的参数
            string str = "I'm the parameter of the callback function!";

            for (int i = 0; i < 3; i++)
            {
                userInfo = new UserInfo();
                userInfo.Name = "Brambling" + i.ToString();
                userInfo.Age = 33 + i;

                //传入参数并执行异步委托,并设置回调函数
                IAsyncResult result = myDelegate.BeginInvoke(userInfo, asyncCallback, str);
            }

            Console.WriteLine("Main thread working...");
            Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Console.WriteLine();
            
            Console.ReadKey();
        }

        public static void Complete(IAsyncResult result)
        {
            UserInfo userInfoRes = null;

            AsyncResult asyncResult = (AsyncResult)result;

            //获取在其上调用异步调用的委托对象
            MyDelegate myDelegate = (MyDelegate)asyncResult.AsyncDelegate;
            
            //结束在其上调用的异步委托,并获取返回值
            userInfoRes = myDelegate.EndInvoke(result);

            Console.WriteLine("My name is " + userInfoRes.Name);
            Console.WriteLine("I'm " + userInfoRes.Age + " years old this year");
            Console.WriteLine("Thread ID is:" + userInfoRes.ThreadId);

            //获取回调函数的参数
            string str = result.AsyncState as string;
            Console.WriteLine(str);
        }
    }

    public class ThreadDemoClass
    {
        public UserInfo Run(UserInfo userInfo)
        {
            userInfo.ThreadId = Thread.CurrentThread.ManagedThreadId;

            Console.WriteLine("Child thread working...");
            Console.WriteLine("Child thread ID is:" + userInfo.ThreadId);
            Console.WriteLine();

            return userInfo;
        }
    }

执行结果:

回调函数的参数也是 object 类型的,我这里用的是一个 string 类型,可是它也能够是自定义类型的参数。

本篇博客到此结束,在写这篇博客的同时也让我我的对多线程编程加深了理解,多线程编程的知识点还有不少,后面再继续与你们分享。

 

参考:

http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html#t1

相关文章
相关标签/搜索