• .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             }
  • 相关阅读:
    Merge into使用详解( 同时执行inserts和updates操作 )
    sql执行计划解析案例(二)
    包的定义和导入-----package
    jQuery练习实例(四)
    打印沙漏形
    Java的RandomAccessFile
    农场有头大母牛,每年生头小母牛,小母牛五年后生小母牛,问20年后农场一共有多少头牛?(用面向对象的思想)
    关于编译Lambda时报告返回的为void的错误
    银行对账
    mysql查询随机几条数据(速度快)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/7055901.html
Copyright © 2020-2023  润新知