zoukankan      html  css  js  c++  java
  • 异步Async

    1.c#异步介绍

      异步必须基于委托,有委托才有异步

      新建一个window Form程序MyAsync,添加一个按钮,(name)=btnAsync

      后台代码如下:

    using System;
    using System.Windows.Forms; using System.Threading; namespace MyAsync { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private delegate void DoSomethingHandler(string name);//定义一个委托函数 private void btnAsync_Click(object sender, EventArgs e)//btnAsync的点击事件 { DoSomethingHandler method = new DoSomethingHandler(DoSomething);
           for (int i = 1; i <= 5; i++) { string name = string.Format("Async_{0}", i); method.BeginInvoke(name, null, null);//新建一个子线程,异步调用 } }private void DoSomething(string name)//调用函数 { Console.WriteLine("{0}正在执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("{0}结束执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId); } } }

       ps:在window Form,我们可以把启动项目的输出类型改成——控制台应用程序,这样在点击事件时,可以看到代码的执行过程

      如:

      

      

      执行结果:

      

      异步调用特点:无序、高效、线程并发

     2.异步进阶:对BeginInvoke(name, null, null)参数的了解

      

      第一个参数:委托函数的参数

      第二个参数:AsyncCallback类型的参数,表示异步回调,只有执行完调用函数,才会去执行回调函数

      第三个参数:object类型的参数,用于给回调函数传参,在回调函数使用  [回调函数的参数名].AsyncState 可以使用这个参数,如以下代码的result.AsyncState

    using System;
    using System.Windows.Forms; using System.Threading; namespace MyAsync { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private delegate void DoSomethingHandler(string name);//调用函数的委托 //public delegate void AsyncCallback(IAsyncResult ar);//回调委托,在AsyncCallback上查询定义可以查到,不能在这块重复定义 private void btnAsync_Click(object sender, EventArgs e) { DoSomethingHandler method = new DoSomethingHandler(DoSomething);//声明两个委托 AsyncCallback asyncCallback = new AsyncCallback(ThreadCallback);
    for (int i = 1; i <= 5; i++) { string name = string.Format("Async_{0}", i); method.BeginInvoke(name, asyncCallback, "param");//执行完委托的DoSomething方法后才执行回调函数ThreadCallback } } private void ThreadCallback(IAsyncResult result)//回调函数 { Console.WriteLine("{0}执行了ThreadCallback,参数有{1}", Thread.CurrentThread.ManagedThreadId,result.AsyncState); } private void DoSomething(string name)//调用函数 { Console.WriteLine("{0}正在执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("{0}结束执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId); } } }

       扩展:当我们把method.BeginInvoke赋值给一个变量的时候,这个变量代表的就是回调函数的参数(为了方便观察,这里去掉了循环,并修改了btnAsync_Click事件)

         private void btnAsync_Click(object sender, EventArgs e)
            {
                DoSomethingHandler method = new DoSomethingHandler(DoSomething);
                AsyncCallback asyncCallback = new AsyncCallback(ThreadCallback);
                string name = "Async_{0}";
                IAsyncResult result= method.BeginInvoke(name, asyncCallback, "param");//执行完委托的DoSomething方法后才执行回调函数ThreadCallback
                Console.WriteLine(result.AsyncState);
            }

      通过这个结果,我们可以看到,1代表是并不是这个函数的返回结果,而是输入参数,即1和2是等价的,是由3中两个参数决定的(我也知道这个很神奇)

      而且,因为是异步请求,BeginInvoke新开的一个子线程和主线程是同时进行的,所以Console.WriteLine(result.AsyncState);可以先被执行,即先打印了param

    3.带返回值的异步调用

      修改调用函数,使其有return

      在调用函数被修改后,调用函数委托也得修改

      method.EndInvoke(result)方法可以接收返回值,result是IAsyncResult类型,也就是上图的1

      因为1与2等价,所以在回调函数里面也可以使用method.EndInvoke(result)查看调用函数的返回值(使用lambda表达式,不然无法使用method,下面代码的红字),还有一次异步调用只能使用一次method.EndInvoke(result)

      method.EndInvoke(result)方法必须等调用函数执行完才能执行,当调用函数还没return时会卡住界面,即卡住主线程

    using System;using System.Windows.Forms;
    using System.Threading;
    namespace MyAsync
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            private delegate int DoSomethingHandler(string name);
            private void btnAsync_Click(object sender, EventArgs e)
            {
                DoSomethingHandler method = new DoSomethingHandler(DoSomething);
                AsyncCallback asyncCallback = new AsyncCallback(ThreadCallback);
                string name = "Async";
                IAsyncResult result=method.BeginInvoke(name, asyncCallback, "param");//执行完委托的调用函数DoSomething后才执行回调函数ThreadCallback
                //--------------------如果这里有代码,是可以和BeginInvoke新开的子线程同步执行的----------------//
                //-------但是执行到EndInvoke的时候,若是调用函数还没执行完,无法进行下一步,会卡住界面---------//
                while (!result.IsCompleted)
                {
                    Console.WriteLine("调用函数还没执行完,请耐心等待....");
                    Thread.Sleep(500);//每0.5秒打印一次
                }
                //-------------可以使用IsCompleted判断是否执行完毕,避免用户以为卡机,关闭程序------------------//
                int i = method.EndInvoke(result);//等调用函数执行完才会执行
                Console.WriteLine("这里是返回值={0}",i);
            }
            private void ThreadCallback(IAsyncResult result)//回调函数
            {
                Console.WriteLine("{0}执行了ThreadCallback,参数有{1}", Thread.CurrentThread.ManagedThreadId,result.AsyncState);
            }
            private int DoSomething(string name)//调用函数
            {
                Console.WriteLine("{0}正在执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(1000);
                Console.WriteLine("{0}结束执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId);
                return 1;
            }
        }
    }

      结果:

     4.扩展:使用lambda

    using System;using System.Windows.Forms;
    using System.Threading;
    namespace MyAsync
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            private delegate int DoSomethingHandler(string name);
            private void btnAsync_Click(object sender, EventArgs e)
            {
                DoSomethingHandler method = new DoSomethingHandler(DoSomething);
                //AsyncCallback asyncCallback = new AsyncCallback(ThreadCallback);//使用lambda后可删除回调委托和回调函数
                string name = "Async";
    
                method.BeginInvoke(name, t=>
                {//回调函数
                    Console.WriteLine("{0}执行了ThreadCallback,参数有{1}", Thread.CurrentThread.ManagedThreadId, t.AsyncState);
                    int i = method.EndInvoke(t);//回掉函数内部打印返回值
                    Console.WriteLine("这里是返回值={0}", i);
                }, "param");
            }
            //private void ThreadCallback(IAsyncResult result)//使用lambda后可删除回调委托和回调函数
            //{
            //    Console.WriteLine("{0}执行了ThreadCallback,参数有{1}", Thread.CurrentThread.ManagedThreadId,result.AsyncState);
            //}
            private int DoSomething(string name)//调用函数
            {
                Console.WriteLine("{0}正在执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(1000);
                Console.WriteLine("{0}结束执行,线程ID={1}", name, Thread.CurrentThread.ManagedThreadId);
                return 1;
            }
        }
    }

      结果:

      这个时候因为method.EndInvoke是在回调函数里面使用的,回调函数又是由子线程执行的,所以只会卡住子线程,主线程并不会被卡住,即界面不会卡住

      而且method.EndInvoke对于每个异步操作只能执行一次,如果在子线程和主线程都出现,会报错

      当然,只在子线程或者主线程出现多次也会报错

    5.使用Task框架实现异步

      添加一个新按钮 (name)= AsynsTask

    using System;using System.Windows.Forms;
    using System.Threading;
    namespace MyAsync
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }private void AsynsTask_Click(object sender, EventArgs e)
            {
                Console.WriteLine("接到任务");
                Console.WriteLine("分配任务,用多线程同时进行任务,模拟实际开发中团队同时进行");
                TaskFactory taskFactory = new TaskFactory();//实例化一个TaskFactory任务工厂
                Action act1 = new Action(() => Coding("kxy", "后台"));//用lambda表达式表示一个无参数无返回值的匿名函数
                Action act2 = new Action(() => Coding("fz", "后台"));
                Action act3 = new Action(() => Coding("xx", "数据库"));
                Action act4 = new Action(() => Coding("flt", "前端"));
                Action act5 = new Action(() => Coding("zjx", "前端"));
                Task task1 = taskFactory.StartNew(act1);//Task创建并开启一个线程,执行act
                Task task2 = taskFactory.StartNew(act2);
                Task task3 = taskFactory.StartNew(act3);
                Task task4 = taskFactory.StartNew(act4);
                Task task5 = taskFactory.StartNew(act5);
                List<Task> Ltask = new List<Task>();
                Ltask.Add(task1);
                Ltask.Add(task2);
                Ltask.Add(task3);
                Ltask.Add(task4);
                Ltask.Add(task5);
                Task.WaitAny(Ltask.ToArray());//等待这些线程中某一个执行完再往下执行主线程
                Console.WriteLine("<<<开始进行测试>>>");
                Task.WaitAll(Ltask.ToArray());//等待这些线程执行完成再往下执行主线程
                Console.WriteLine("<<<开发完成,开始部署>>>");
                Console.WriteLine("<<<任务完成>>>");
            }
            private void Coding(string name, string work)
            {
                Console.WriteLine("线程ID={0} {1} 的工作是 {2}", Thread.CurrentThread.ManagedThreadId, name, work);
                Thread.Sleep(1000);
                Console.WriteLine("============线程ID={0} {1} 完成工作 {2}", Thread.CurrentThread.ManagedThreadId, name, work);
            }
        }
    }

  • 相关阅读:
    关于Shipping
    怎么Debug Background Job [转载sdn]
    如何显示IDoc的每个segment/field的具体说明
    underscore.js
    Javascript Style Guide
    [转] Ajax_XMLHttpRequest对象详解
    ExecutingMethodsFromLinkButtonParameters
    使用C#操作MSExcel表格COM
    [转]HttpContext, HttpModules 和 HttpHandlers
    几个实用的Servlet应用例子入门、cookie、session及上传文件
  • 原文地址:https://www.cnblogs.com/wskxy/p/9209759.html
Copyright © 2011-2022 走看看