一. 再谈委托
1. 委托是一个关键字为delegate的自定义类型,通过委托可以把方法以参数的形式传递给另外一个方法,实现插件式的开发模式;
同时调用委托的时候,委托所包含的所有方法都会被实现。
2. 委托的发展历史:new实例化传递方法→直接等于方法名→delegate匿名方法→省略delegate→省略括号中的参数→当只有一个参数省略小括号
→当方法体只有一行,省略大括号
(详见:http://www.cnblogs.com/yaopengfei/p/6959141.html)
3:常用的Action委托和Func委托
A. Action<>委托,无返回值,至少有一个参数的委托
B. Func<>委托,有返回值,可以无参数的委托(当然也可以有参数)
C. Action委托,无参数无返回值的委托
二. 委托的调用
委托的调用分为两种:
A. 同步调用:Invoke方法,方法参数为函数的参数。
B. 异步调用:BeginInvoke方法。
其中无论是哪类调用,都有两类写法:
①:利用Action<>(或Func<>)内置委托,调用的时候赋值。
②:利用Action委托,直接赋值,然后调用。
1 /// <summary>2 /// 执行动作:耗时而已3 /// </summary>4 private void TestThread2(string threadName1, string threadName2)5 {6 Console.WriteLine("线程开始:线程名为:{2}和{3},当前线程的id为:{0},当前时间为:{1},", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName1, threadName2);7 long sum = 0;8 for (int i = 1; i < 999999999; i++)9 { 10 sum += i; 11 } 12 Console.WriteLine("线程结束:线程名为:{2}和{3},当前线程的id为::{0},当前时间为:{1}", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName1, threadName2); 13 }
三. 深入剖析BeginInvoke方法
首先需要明确,该方法参数个数不定, 最后两个参数含义固定,如果不使用的话,需要赋值null;该方法最少两个参数,即方法无参数,这种情况下BeginInvoke中只有两个参数。此外,赋值的方法有几个参数,BeginInvoke中从左开始,新增几个参数。
①. 倒数第二个参数:是有一个参数值无返回值的委托,它代表的含义为,该线程执行完毕后的回调。
②. 倒数第一个参数:向倒数第二个参数(即回调)中传值,需要用AsyncState来接受。
③. 其它参数:即为赋值方法的参数。
注:BeginInvoke的返回值等价于异步回调中的t。
1 private void button13_Click(object sender, EventArgs e)2 {3 Stopwatch watch = new Stopwatch();4 watch.Start();5 Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId);6 7 Action<string> myFunc = this.TestThread;8 IAsyncResult asyncResult = null;9 //参数说明:前面几个参数都是方法的参数值,倒数第二个为异步调用的回调函数,倒数第一个为传给回调函数的参数 10 for (int i = 0; i < 1; i++) 11 { 12 string name = string.Format("button1_Click{0}", i); 13 asyncResult = myFunc.BeginInvoke(name, t => 14 { 15 Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId); 16 //用 t.AsyncState 来获取回调传进来的参数 17 Console.WriteLine("传进来的参数为:{0}", t.AsyncState); 18 19 //测试一下异步返回值的结果 20 Console.WriteLine("异步返回值的结果:{0}", t.Equals(asyncResult)); 21 }, "maru"); 22 } 23 24 watch.Stop(); 25 Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds); 26 27 }
结果:
四. 线程等待的三种方式
1. asyncResult.IsCompleted属性,该方式会存在时间上的误差。
2. WaitOne方法,可以控制一直等待or超时不再等待。
3. EndInvoke方法,官方推荐的线程等待的方式。
以上三种方式的局限性:批量线程等待的时候,不灵活,需要for循环了。
1 private void button14_Click(object sender, EventArgs e)2 {3 Stopwatch watch = new Stopwatch();4 watch.Start();5 Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId);6 7 IAsyncResult asyncResult = null;8 Action<string> myFunc = this.TestThread;9 string name = string.Format("button1_Click{0}", 111); 10 asyncResult = myFunc.BeginInvoke(name, t => 11 { 12 Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId); 13 //用 t.AsyncState 来获取回调传进来的参数 14 Console.WriteLine("传进来的参数为:{0}", t.AsyncState); 15 }, "maru"); 16 17 //等待的方式1:会有时间上的误差 18 //while (!asyncResult.IsCompleted) 19 //{ 20 // Console.WriteLine("正在等待中"); 21 //} 22 23 // 等待的方式二: 24 //asyncResult.AsyncWaitHandle.WaitOne();//一直等待 25 //asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待 26 //asyncResult.AsyncWaitHandle.WaitOne(1000);//等待1000毫秒,超时就不等待了 27 28 //等待的方式三: 29 myFunc.EndInvoke(asyncResult); 30 31 watch.Stop(); 32 Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds); 33 34 }
下面是多个线程等待的情况:
1 private void button15_Click(object sender, EventArgs e)2 {3 Stopwatch watch = new Stopwatch();4 watch.Start();5 Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId);6 7 List<IAsyncResult> list = new List<IAsyncResult>();8 9 for (int i = 0; i < 5; i++) 10 { 11 string name = string.Format("button1_Click{0}", i); 12 Action myFunc = () => 13 { 14 TestThread2(name, name); 15 }; 16 var asyncResult = myFunc.BeginInvoke(null, null); 17 list.Add(asyncResult); 18 } 19 20 //下面是线程等待 21 foreach (var item in list) 22 { 23 item.AsyncWaitHandle.WaitOne(-1); 24 } 25 26 watch.Stop(); 27 Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds); 28 }
结果: