进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法访问另一个 进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进 程可以理解为一个程序的基本边界。
应用程序域(AppDomain)是一个程序运行的逻辑区域,它可以视为一个轻量级的进程,.NET的程序集正是在应用程序域中运行的,一个进程可 以包含有多个应用程序域,一个应用程序域也可以包含多个程序集。在一个应用程序域中包含了一个或多个上下文context,使用上下文CLR就能够把某些 特殊对象的状态放置在不同容器当中。
线程(Thread)是进程中的基本执行单元,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法 作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状 态信息。
进程、应用程序域、线程的关系如下图,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。
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显示当前线程信息
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 }
class Program { static void Main(string[] args) { //Thread th = new Thread(Sleep); Thread th = new Thread(new ThreadStart(Sleep)); th.Start(); for (int i = 0; i < 10; i++) { Console.WriteLine("这里是主线程在工作" + i); } } //模拟执行长时间的任务 static void Sleep() { ThreadMessage("Sleep"); Console.WriteLine("我还没有执行完呢,请耐心等待...."); Thread.Sleep(3000); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }
运行后你会发现,Sleep()方法 还没有执行完(睡眠3分钟模拟长时间任务),主线程已经执行完成. 这就是多线程的好处.
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //Thread th = new Thread(Sleep); 6 Thread th = new Thread(new ParameterizedThreadStart(Sleep)); 7 th.Start("Sleep"); 8 for (int i = 0; i < 10; i++) 9 { 10 Console.WriteLine("这里是主线程在工作" + i); 11 } 12 13 } 14 15 //模拟执行长时间的任务 16 static void Sleep(object state) 17 { 18 string name = (string)state; 19 ThreadMessage(name); 20 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 21 Thread.Sleep(3000); 22 } 23 24 static void ThreadMessage(string data) 25 { 26 string message = string.Format("ThreadName is {0} ThreadId is:{1}", 27 data, Thread.CurrentThread.ManagedThreadId); 28 Console.WriteLine(message); 29 } 30 }
运行结果和上面那差不多一样.
想必大家发现了这2个例子中都有一句注释的Thread th = new Thread(Sleep),这是为什么呢? 你可以把注释打开,下面的th注释掉,运行下,你会发现,两次都一样,这又是为什么呢?因为在实例化th的时候,有4个重载方法,其中2个就是ThreadStart和ParameterizedThreadStart
我们都知道ThreadStart和ParameterizedThreadStart和2个委托,而委托最大的作用就是传递一个方法, 在c#2.0就引入了匿名方法(3.0以及更高,lambda表达式取代了匿名方法)我们来看看匿名方法在这里能给我们带来点什么惊喜(方便)
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //Thread th = new Thread(delegate() { //在new ThreadStart()中 6 // ShowMessage("ShowMessage"); 7 //}); 8 //Thread th = new Thread(new ThreadStart(delegate() { 9 // ShowMessage("ShowMessage"); 10 //})); 11 //Thread th = new Thread(new ThreadStart(() => 12 //{ 13 // ShowMessage("ShowMessage"); 14 //})); 15 16 Thread th = new Thread(delegate(object o) //new ParameterizedThreadStart()中的 17 { 18 ShowMessage("ShowMessage"); 19 }); 20 //Thread th = new Thread(new ParameterizedThreadStart(delegate(object o) { 21 // ShowMessage("ShowMessage"); 22 //})); 23 //Thread th = new Thread(t => ShowMessage("show")); 24 th.Start(); 25 for (int i = 0; i < 10; i++) 26 { 27 Console.WriteLine("这里是主线程在工作" + i); 28 } 29 } 30 31 //模拟执行长时间的任务 32 static void Sleep(object state) 33 { 34 string name = (string)state; 35 ThreadMessage(name); 36 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 37 Thread.Sleep(3000); 38 } 39 40 //这里的参数不为object,你可以定义任何类型,任何参数 41 static void ShowMessage(string message) 42 { 43 ThreadMessage(message); 44 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 45 Thread.Sleep(3000); 46 } 47 48 static void ThreadMessage(string data) 49 { 50 string message = string.Format("ThreadName is {0} ThreadId is:{1}", 51 data, Thread.CurrentThread.ManagedThreadId); 52 Console.WriteLine(message); 53 } 54 }
上面的几种方式都可以用. 我为什么要用个ShowMessage(string msg) 方法来测试呢 ? 细心你会发现ParameterizedThreadStart委托他定义的参数为object,用匿名函数就可以解决这问题.如果你对委托,匿名函数不太熟悉的话,你就要补习一下关于委托的知识了.
注意以上两个例子都没有使用Console.ReadKey(),但系统依然会等待异步线程完成后才会结束。这是因为使用Thread.Start()启动的线程默认为前台线程,而系统必须等待所有前台线程运行结束后,应用程序域才会自动卸载。
在第二节曾经介绍过线程Thread有一个属性IsBackground,通过把此属性设置为true,就可以把线程设置为后台线程!这时应用程序域将在主线程完成时就被卸载(主线程关闭),而不会等待异步线程的运行。
Thread.Sleep()大家都很熟悉了,休眠多长时间,里面是毫秒.1秒=1000毫秒. Join() 表面意思是把线程加入,也就是这线程完事后主线程才被卸载.
你可以把子线程设置成后台线程,然后调用这个方法,就不会发现'一闪而过'的现象了. Thread.Suspend()与 Thread.Resume()是在Framework1.0 就已经存在的老方法了,它们分别可以挂起、恢复线程。但在Framework2.0中就已经明确排斥这两个方法。这是因为一旦某个线程占用了已有的资源,再使用Suspend()使线程长期处于挂起状态,当在其他线程调用这些资源的时候就会引起死锁!所以在没有必要的情况下应该避免使用这两个方法。
若想终止正在运行的线程,可以使用Abort()方法。在使用Abort()的时候,将引发一个特殊异常 ThreadAbortException 。
若想在线程终止前恢复线程的执行,可以在捕获异常后 ,在catch(ThreadAbortException ex){...} 中调用Thread.ResetAbort()取消终止。
下面的例子是 终止线程和取消终止的例子(拷贝风尘浪子的)
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine("Main threadId is:" + 6 Thread.CurrentThread.ManagedThreadId); 7 8 Thread thread = new Thread(new ThreadStart(AsyncThread)); 9 thread.Start(); 10 Console.ReadKey(); 11 } 12 13 //以异步方式调用 14 static void AsyncThread() 15 { 16 try 17 { 18 string message = string.Format("\nAsync threadId is:{0}", 19 Thread.CurrentThread.ManagedThreadId); 20 Console.WriteLine(message); 21 22 for (int n = 0; n < 10; n++) 23 { 24 //当n等于4时,终止线程 25 if (n >= 4) 26 { 27 Thread.CurrentThread.Abort(n); 28 } 29 Thread.Sleep(300); 30 Console.WriteLine("The number is:" + n.ToString()); 31 } 32 } 33 catch (ThreadAbortException ex) 34 { 35 //输出终止线程时n的值 36 if (ex.ExceptionState != null) 37 Console.WriteLine(string.Format("Thread abort when the number is: {0}!", 38 ex.ExceptionState.ToString())); 39 40 //取消终止,继续执行线程 41 Thread.ResetAbort(); 42 Console.WriteLine("Thread ResetAbort!"); 43 } 44 45 //线程结束 46 Console.WriteLine("Thread Close!"); 47 } 48 }
使用ThreadStart与ParameterizedThreadStart建立新线程非常简单,但通过此方法建立的线程难于管理,若建立过多的线程反而会影响系统的性能。
有 见及此,.NET引入CLR线程池这个概念。CLR线程池并不会在CLR初始化的时候立刻建立线程,而是在应用程序要创建线程来执行任务时,线程池才初始 化一个线程。线程的初始化与其他的线程一样。在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时, 线程池里挂起的线程就会再度激活执行任务。这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销.
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() 方法,二是通过委托(异步操作是加入在线程池中的),下面将逐一细说。
ThreadPool线程池中包含有两个静态方法可以直接启动工作者线程:
一为 ThreadPool.QueueUserWorkItem(WaitCallback)
二为 ThreadPool.QueueUserWorkItem(WaitCallback,Object)
先把WaitCallback委托指向一个带有Object参数的无返回值方法,再使用 ThreadPool.QueueUserWorkItem(WaitCallback) 就可以异步启动此方法,此时异步方法的参数被视为null 。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 ThreadPool.QueueUserWorkItem(new WaitCallback(Sleep));
7 for (int i = 0; i < 10; i++) 8 { 9 Console.WriteLine("这里是主线程在工作" + i); 10 } 11 Console.ReadKey(); 12 } 13 14 static void Sleep(object state) 15 {
17 ThreadMessage("sleep"); 18 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 19 Thread.Sleep(3000); 20 } 21
28 29 static void ThreadMessage(string data) 30 { 31 string message = string.Format("ThreadName is {0} ThreadId is:{1}", 32 data, Thread.CurrentThread.ManagedThreadId); 33 Console.WriteLine(message); 34 } 35 }
这是个不带参数的, ThreadPool.QueueUserWorkItem()有2个参数,第二个是个object类型
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person person = new Person() 6 { 7 Name = "Somnus", 8 Age = 10 9 }; 10 ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage),person); 11 for (int i = 0; i < 10; i++) 12 { 13 Console.WriteLine("这里是主线程在工作" + i); 14 } 15 Console.ReadKey(); 16 } 17 18 static void ShowMessage(object state) 19 { 20 Person person = (Person)state; 21 Console.WriteLine("学生姓名是{0},年龄为{1}",person.Name,person.Age); 22 Thread.Sleep(3000); 23 } 24 } 25 26 class Person 27 { 28 public string Name { get; set; } 29 public int Age { get; set; } 30 //public WaitHandle Wait { get; set; } 31 }
我们创建了一个Person类,在加入线程池的时候,我们传了一个person , 在ShowMessage()方法中,得到传过来的person,并显示人的信息.
多线程执行任务的时候,有时候总是要出错嘛, 重要的是,我们能catch的住异常.
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person person = new Person() 6 { 7 Name = "Somnus", 8 Age = 10 9 }; 10 try 11 { 12 ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person); 13 } 14 catch (Exception ex) 15 { 16 //写日志 17 } 18 for (int i = 0; i < 10; i++) 19 { 20 Console.WriteLine("这里是主线程在工作" + i); 21 } 22 Console.ReadKey(); 23 } 24 25 static void ShowMessage(object state) 26 { 27 Person person = (Person)state; 28 throw new Exception("这里出错了,快catch住我"); 29 Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age); 30 Thread.Sleep(3000); 31 } 32 } 33 34 class Person 35 { 36 public string Name { get; set; } 37 public int Age { get; set; } 38 //public WaitHandle Wait { get; set; } 39 }
这样? No~ 结果会差强人意的,我们并没有catch住他. 那该在哪try呢. 对, 在委托调用那方法中. 我们这样干,
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person person = new Person() 6 { 7 Name = "Somnus", 8 Age = 10 9 }; 10 11 ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person); 12 13 for (int i = 0; i < 10; i++) 14 { 15 Console.WriteLine("这里是主线程在工作" + i); 16 } 17 Console.ReadKey(); 18 } 19 20 static void ShowMessage(object state) 21 { 22 try 23 { 24 Person person = (Person)state; 25 throw new Exception("这里出错了,快catch住我"); 26 Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age); 27 Thread.Sleep(3000); 28 } 29 catch (Exception ex) 30 { 31 //写日志 32 } 33 } 34 } 35 36 class Person 37 { 38 public string Name { get; set; } 39 public int Age { get; set; } 40 //public WaitHandle Wait { get; set; } 41 }
这样,程序就不会报错了.
如果有好几个任务方法, 那我们要写好几个try{}catch{}这样肯定不是我们想要的,看看下面的封装
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person person = new Person() 6 { 7 Name = "Somnus", 8 Age = 10 9 }; 10 ThreadExecutor.Execute(new WaitCallback(ShowMessage),person); 11 for (int i = 0; i < 10; i++) 12 { 13 Console.WriteLine("这里是主线程在工作" + i); 14 } 15 Console.ReadKey(); 16 } 17 18 static void ShowMessage(object state) 19 { 20 Person person = (Person)state; 21 throw new Exception("这里出错了,快catch住我"); 22 Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age); 23 Thread.Sleep(3000); 24 } 25 } 26 27 class Person 28 { 29 public string Name { get; set; } 30 public int Age { get; set; } 31 //public WaitHandle Wait { get; set; } 32 } 33 34 public class ThreadExecutor 35 { 36 37 public static bool Execute(System.Threading.WaitCallback callback, object state) 38 { 39 try 40 { 41 return System.Threading.ThreadPool.QueueUserWorkItem((data) => 42 { 43 try 44 { 45 callback(data); 46 } 47 catch (Exception ex) 48 { 49 //写日志 50 } 51 }, state); 52 } 53 catch (Exception e) 54 { 55 //写日志 56 } 57 return false; 58 } 59 public static bool Execute(System.Threading.WaitCallback callback) 60 { 61 try 62 { 63 return System.Threading.ThreadPool.QueueUserWorkItem((data) => 64 { 65 try 66 { 67 callback(data); 68 } 69 catch (Exception ex) 70 { 71 //写日志 72 } 73 }); 74 } 75 catch (Exception e) 76 { 77 //写日志 78 } 79 return false; 80 } 81 }
其中Execute()方法就是把ThreadPool.QueueUserWorkItem用匿名函数的方法进行了封装,catch住了异常.
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 WaitHandle[] waits = new WaitHandle[2] 7 { 8 new AutoResetEvent(false), 9 new AutoResetEvent(false) 10 }; 11 Person person = new Person() 12 { 13 Name = "Somnus", 14 Age = 10, 15 Wait=waits[0] 16 }; 17 Person person2 = new Person() 18 { 19 Name = "cnblogs", 20 Age = 20, 21 Wait = waits[1] 22 }; 23 ThreadExecutor.Execute(new WaitCallback(ShowMessage),person); 24 ThreadExecutor.Execute(new WaitCallback(ShowMessage), person2); 25 for (int i = 0; i < 10; i++) 26 { 27 Console.WriteLine("这里是主线程在工作" + i); 28 } 29 WaitHandle.WaitAll(waits); 30 //WaitHandle.WaitAny(waits); 31 } 32 33 static void ShowMessage(object state) 34 { 35 Person person = (Person)state; 36 AutoResetEvent are = (AutoResetEvent)person.Wait; 37 Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age); 38 Thread.Sleep(3000); 39 are.Set(); 40 } 41 } 42 43 class Person 44 { 45 public string Name { get; set; } 46 public int Age { get; set; } 47 public WaitHandle Wait { get; set; } 48 } 49 50 public class ThreadExecutor 51 { 52 53 public static bool Execute(System.Threading.WaitCallback callback, object state) 54 { 55 try 56 { 57 return System.Threading.ThreadPool.QueueUserWorkItem((data) => 58 { 59 try 60 { 61 callback(data); 62 } 63 catch (Exception ex) 64 { 65 //写日志 66 } 67 }, state); 68 } 69 catch (Exception e) 70 { 71 //写日志 72 } 73 return false; 74 } 75 public static bool Execute(System.Threading.WaitCallback callback) 76 { 77 try 78 { 79 return System.Threading.ThreadPool.QueueUserWorkItem((data) => 80 { 81 try 82 { 83 callback(data); 84 } 85 catch (Exception ex) 86 { 87 //写日志 88 } 89 }); 90 } 91 catch (Exception e) 92 { 93 //写日志 94 } 95 return false; 96 } 97 }
WaitHandle.WaitAll(),最大可监测64个WaitHandler ,如果你需要的多线程比较多,你可以分批,中间Sleep()一段时间,就可以了.
WaitHandle.WaitAny(),其中某一个线程完成后,就退出主线程.
使用CLR线程池中的工作者线程,最灵活最常用的方式就是使用委托的异步方法.委托包括下面3个重要方法:Invoke(),BeginInvoke(),EndInvoke()
当调用Invoke()方法时,对应此委托的所有方法都会被执行。而BeginInvoke与EndInvoke则支持委托方法的异步调用,由BeginInvoke启动的线程都属于CLR线程池中的工作者线程。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 SleepDelegate sleepDelegate = new SleepDelegate(Sleep); 6 IAsyncResult result= sleepDelegate.BeginInvoke("Sleep",null,null); 7 string data = sleepDelegate.EndInvoke(result); 8 Console.WriteLine(data); 9 Console.ReadKey(); 10 } 11 12 delegate string SleepDelegate(object o); 13 14 //模拟执行长时间的任务 15 static string Sleep(object state) 16 { 17 string name = (string)state; 18 ThreadMessage(name); 19 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 20 Thread.Sleep(3000); 21 return "Hello" + name; 22 } 23 24 static void ThreadMessage(string data) 25 { 26 string message = string.Format("ThreadName is {0} ThreadId is:{1}", 27 data, Thread.CurrentThread.ManagedThreadId); 28 Console.WriteLine(message); 29 } 30 }
委托还有个可以调用回调函数的
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 SleepDelegate sleepDelegate = new SleepDelegate(Sleep); 6 IAsyncResult result= sleepDelegate.BeginInvoke("Sleep",new AsyncCallback(Complete),"这里可以传个object值啊"); 7 Console.ReadKey(); 8 } 9 10 delegate string SleepDelegate(object o); 11 12 //模拟执行长时间的任务 13 static string Sleep(object state) 14 { 15 string name = (string)state; 16 ThreadMessage(name); 17 Console.WriteLine("我还没有执行完呢,请耐心等待...."); 18 Thread.Sleep(3000); 19 return "Hello" + name; 20 } 21 22 static void Complete(IAsyncResult iresult) 23 { 24 AsyncResult result = (AsyncResult)iresult; 25 SleepDelegate sleepDelegate = (SleepDelegate)result.AsyncDelegate; 26 string obj = (string)result.AsyncState; 27 string data = sleepDelegate.EndInvoke(result); 28 Console.WriteLine(obj + data); 29 } 30 31 static void ThreadMessage(string data) 32 { 33 string message = string.Format("ThreadName is {0} ThreadId is:{1}", 34 data, Thread.CurrentThread.ManagedThreadId); 35 Console.WriteLine(message); 36 } 37 }