.Net进阶系列(11)-异步多线程(委托BeginInvoke)(被替换)

一. BeginInvoke最后两个参数的含义

  倒数第二个参数:指该线程执行完毕后的回调函数;倒数第一个参数:可以向回调函数中传递参数。

     下面以一段代码说明:

 1         /// <summary>
 2         /// 执行动作:耗时而已
 3         /// </summary>
 4         private void TestThread(string threadName)
 5         {
 6             Console.WriteLine("线程开始:线程名为:{2},当前线程的id为:{0},当前时间为:{1},", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName);
 7             long sum = 0;
 8             for (int i = 1; i < 999999999; i++)
 9             {
10                 sum += i;
11             }
12             Console.WriteLine("线程结束:线程名为:{2},当前线程的id为::{0},当前时间为:{1}", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName);
13         }
14      private void button1_Click_1(object sender, EventArgs e)
15         {
16             Stopwatch watch = new Stopwatch();
17             watch.Start();
18             Console.WriteLine("----------------- 二.委托的异步调用测试  --------------------------");
19             Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0}  --------------------------", Thread.CurrentThread.ManagedThreadId);
20 
21             #region 2. 异步回调和异步参数
22             {
23                 Action<string> myFunc = this.TestThread;
24                 //参数说明:前面几个参数都是方法的参数值,倒数第二个为异步调用的回调函数,倒数第一个为传给回调函数的参数
25                 for (int i = 0; i < 5; i++)
26                 {
27                     string name = string.Format("button1_Click{0}", i);
28                     myFunc.BeginInvoke(name, t =>
29                     {
30                         Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId);
31                         //用 t.AsyncState 来获取回调传进来的参数
32                         Console.WriteLine("传进来的参数为:{0}", t.AsyncState);
33                     }, "maru");
34                 }
35             }
36             #endregion
37 
38             watch.Stop();
39             Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0}  总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
40         }

  结果:

二. 异步调用的三种书写

   在上述代码中,我们发现BeginInvoke中,除了我们介绍的最后两个参数外,还有一个参数,传递进去了name,细心的人会发现,正式函数TestThread所需的参数,那么向函数中传递参数到底是在赋值委托时传递,还是委托调用时传递呢?

     答案是两者皆可,如果在函数赋值委托时候传递,那么委托调用的时候,BeginInvoke只有最后两个参数。如果在委托调用的时候传递,BeginInvoke除了最后两个参数外,函数本身有几个参数,BeginInvoke前面将多出几个参数位置。

 1         /// <summary>
 2         /// 两个参数
 3         /// </summary>
 4         public static void TestThread(string txt1, string txt2)
 5         {
 6             Console.WriteLine("线程开始:测试参数为:{0}和{1},当前线程的id为:{2}", txt1, txt2, System.Threading.Thread.CurrentThread.ManagedThreadId);
 7             long sum = 0;
 8             for (int i = 1; i < 999999999; i++)
 9             {
10                 sum += i;
11             }
12             Console.WriteLine("线程结束:测试参数为:{0}和{1},当前线程的id为:{2}", txt1, txt2, System.Threading.Thread.CurrentThread.ManagedThreadId);
13         }
14           //1. 方式一(使用多重载Action<>委托,函数的参数在BeginInvoke中传入)
15             {
16                 Action<string, string> act = TestThread;
17                 IAsyncResult iTest = act.BeginInvoke("参数1", "参数2", t =>
18                    {
19                        Console.WriteLine("我是线程执行后的回调");
20                        Console.WriteLine(t.AsyncState);
21 
22                    }, "我是传递参数的位置");
23             }
24 
25             //2. 方式二(使用Action委托,将参数值直接写在方法中,则无须向BeginInvoke中传入)
26             {
27                 Action act2 = () => TestThread("参数1", "参数2");
28                 act2.BeginInvoke(t =>
29                 {
30                     Console.WriteLine("我是线程执行后的回调");
31                     Console.WriteLine(t.AsyncState);
32 
33                 }, "我是传递参数的位置");
34             }
35 
36             //3. 方式三(下面两个等价,只不过是第一个省略{},在函数体中将方法写入)
37             {
38                 Action<string, string> act3 = (a, b) => TestThread(a, b);
39                 //Action<string, string> act3 = (a, b) =>
40                 //{
41                 //    TestThread(a, b);
42                 //};
43                 IAsyncResult iTest = act3.BeginInvoke("参数1", "参数2", null, null);
44             }

三. 线程等待的三种方式

   关于利用委托开启多线程,其线程等待有三种方式:endInvoke、waitone、IsCompleted,推荐使用endInvoke这种方式。

 1      private void button1_Click_1(object sender, EventArgs e)
 2         {
 3             Stopwatch watch = new Stopwatch();
 4             watch.Start();
 5             Console.WriteLine("----------------- 二.委托的异步调用测试  --------------------------");
 6             Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0}  --------------------------", Thread.CurrentThread.ManagedThreadId);
 7 
 8             #region 4. 异步等待
 9             {
10                 IAsyncResult asyncResult = null;
11                 Action<string> myFunc = this.TestThread;
12                 string name = string.Format("button1_Click{0}", 111);
13                 asyncResult = myFunc.BeginInvoke(name, t =>
14                  {
15                      Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId);
16                      //用 t.AsyncState 来获取回调传进来的参数
17                      Console.WriteLine("传进来的参数为:{0}", t.AsyncState);
18                  }, "maru");
19 
20                 //等待的方式1:会有时间上的误差
21                 //while (!asyncResult.IsCompleted)
22                 //{
23                 //    Console.WriteLine("正在等待中");
24                 //}
25 
26                 // 等待的方式二:
27                 //asyncResult.AsyncWaitHandle.WaitOne();//一直等待
28                 //asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待
29                 //asyncResult.AsyncWaitHandle.WaitOne(1000);//等待1000毫秒,超时就不等待了
30 
31                 //等待的方式三:
32                 myFunc.EndInvoke(asyncResult);
33 
34 
35             }
36             #endregion
37 
38             watch.Stop();
39             Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0}  总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
40         }
41         #endregion

    运行上述代码,我们会发现,主界面又被卡在了,这正好印证了主线程在等待,结果如下:

   

四. 多个线程的等待

    上面介绍的是单个线程的等待,有三种方式,那么如果同时开启了多个线程,主线程需要等待这多个线程,这时需要自己写循环,来进行线程等待。

 1             {
 2                 List<IAsyncResult> list = new List<IAsyncResult>();
 3                 for (int i = 0; i < 5; i++)
 4                 {
 5                     string name1 = string.Format("ypf1-{0}", i);
 6                     string name2 = string.Format("ypf2-{0}", i);
 7                     Action act = () => TestThread(name1, name2);
 8                     IAsyncResult ir = act.BeginInvoke(null, null);
 9                     list.Add(ir);
10                 }
11                 //利用委托进行的异步多线程,采用上述方式二的等待最合理的
12                 //缺点:整体上需要写循环,麻烦
13                 foreach (var item in list)
14                 {
15                     item.AsyncWaitHandle.WaitOne();
16                 }
17             }

 

posted @ 2017-06-20 19:24  Yaopengfei  阅读(560)  评论(0编辑  收藏  举报