首页 > 学院 > 开发设计 > 正文

阅读《LEARNING HARD C#学习笔记》知识点总结与摘要五

2019-11-17 02:23:08
字体:
来源:转载
供稿:网友

阅读《LEARNING HARD C#学习笔记》知识点总结与摘要五

本篇文章主要是总结异步编程的知识点,也是本系列的最后一篇文章,每一个知识点我都有写出示例代码,方便大家理解,若发现有误或不足之处还请指出,由于书中作者对此知识点讲解过于简单,所以在写这篇文章时本人参考与学习了网上许多大牛们的经验,在此感谢那些愿意分享的人们,谢谢!

二十三.异步编程

APM(异步编程模型):若类实现了返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法,则表明该类支持异步编程模型。如:委托类型定义了BeginInvoke与EndInvoke方法,所以所有的委托类型都实现了异步编程模型;

调用方法代码如下(以读取文件内容为例):

第一种方法(先调用BeginRead方法,再调用EndRead方法):

FileStream fs = new FileStream("文件路径", FileMode.Open);            byte[] data = new byte[fs.Length];            IAsyncResult result = fs.BeginRead(data, 0, data.Length,null, null);            fs.EndRead(result);            fs.Close();            fs.Dispose();            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();            string readContent = UTF8.GetString(data);            Console.WriteLine(readContent);            Console.Read();

注意:调用EndRead方法时,会阻塞当前调用的线程,直到处理完成,若在UI线程中调用,将会出现UI假死现象;

第二种方法(先调用BeginRead方法,再通过IAsyncResult. AsyncWaitHandle得到WaitHandle对象,然后执行WaitHandle. WaitOne方法来等待异步执行完成,最后执行EndRead方法):

FileStream fs = new FileStream("文件路径", FileMode.Open);            byte[] data = new byte[fs.Length];            IAsyncResult result = fs.BeginRead(data, 0, data.Length, null, null);            WaitHandle readWait=result.AsyncWaitHandle;            readWait.WaitOne();fs.EndRead(result);            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();            string readContent = UTF8.GetString(data);            Console.WriteLine(readContent);            Console.Read();

注意:调用WaitOne方法时,会阻塞当前调用的线程,直到处理完成,若在UI线程中调用,将会出现UI假死现象;

第三种方法(先调用BeginRead方法,再循环判断IAsyncResult. IsCompleted,最后执行EndRead方法):

FileStream fs = new FileStream("文件路径", FileMode.Open);            byte[] data = new byte[fs.Length];            IAsyncResult result = fs.BeginRead(data, 0, data.Length, null, null);            while(!result.IsCompleted)            {                Thread.Sleep(1000);            }            fs.EndRead(result);            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();            string readContent = UTF8.GetString(data);            Console.WriteLine(readContent);            Console.Read();

注意:循环判断IsComplete方法时,会阻塞当前调用的线程,直到处理完成[即:IsCompleted=true],若在UI线程中循环判断,将会出现UI假死现象;

第四种方法(先定义回调方法,然后再调用BeginRead方法,调用时传入异步回调方法委托实例及相关数据):

FileStream fs = new FileStream("文件路径", FileMode.Open);            byte[] data = new byte[fs.Length];            IAsyncResult result = fs.BeginRead(data, 0, data.Length, new AsyncCallback(ReadCallback), data);            Console.Read();//回调方法static void ReadCallback(IAsyncResult ar)        {            WaitHandle readWait = ar.AsyncWaitHandle;            byte[] data =( byte[])ar.AsyncState;            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();            string readContent = UTF8.GetString(data);            Console.WriteLine(readContent);        }

说明:第四种由于采用异步委托方法获取得结果,所以不会阻塞调用线程,也就不会出现UI假死现象,当然前面三种方法也可以将耗时逻辑(等待及获取结果)放在方法中,然后在执行时开启新线程,这样可以达到与第四种相同的方法,但个人建议若在UI主线程中实现异步调用,优先考虑采用第四种方法;

EAP(基于事件的异步模式):实现了EAP的类具有一个或多个以Async为后缀的方法,以及对应的Completed事件,并支持异步方法的取消与进度报告。

EAP通过事件、AsyncOperationManager类和AsyncOperation类两个帮助器类实现如下功能:

1) 异步执行耗时的任务。

2) 获得进度报告和增量结果。

3) 支持耗时任务的取消。

4) 获得任务的结果值或异常信息。

5) 更复杂:支持同时执行多个异步操作、进度报告、增量结果、取消操作、返回结果值或异常信息。

对于相对简单的多线程应用程序,BackgroundWorker组件提供了一个简单的解决方案。对于更复杂的异步应用程序,可以考虑实现一个符合基于事件的异步模式的类。

可参见这篇博文:http://www.VEVb.com/heyuquan/archive/2013/04/01/2993085.html

以下是BackgroundWorker组件在控制台程序中的使用方法:

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Consoleapplication1{    class PRogram    {        static int Main(string[] args)        {            Console.Write("Main Thread ID:{0}/n", System.Threading.Thread.CurrentThread.ManagedThreadId);            int workTimes = 100;            BackgroundWorker bgWorker = new BackgroundWorker();            bgWorker.WorkerSupportsCancellation = true;//设置支持异步取消            bgWorker.WorkerReportsProgress = true;//设置支持报告进度            bgWorker.DoWork += bgWorker_DoWork;            bgWorker.ProgressChanged += bgWorker_ProgressChanged;            bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;            bgWorker.RunWorkerAsync(workTimes);//启动异步执行            string input = Console.ReadLine();            if (input == "c" && bgWorker.IsBusy)            {                bgWorker.CancelAsync();            }            Console.Read();            return 0;        }        static void bgWorker_DoWork(object sender, DoWorkEventArgs e)        {            //在新线程中处理该方法            BackgroundWorker bgWorker = sender as BackgroundWorker;            int workTimes = Convert.ToInt32(e.Argument);            for(int i = 0; i <= workTimes;i++ )            {                if (bgWorker.CancellationPending) //如果取消了                {                   e.Cancel = true;                   Console.Write("ThreadID:{0}-->Cancel!/n", System.Threading.Thread.CurrentThread.ManagedThreadId);                    break;                }                else                {                    Console.Write("Thread ID:{0}-->WorkTimes:{1}/n", System.Threading.Thread.CurrentThread.ManagedThreadId, i);                    bgWorker.ReportProgress(i);//投告进度,引发ProgressChanged事件                    System.Threading.Thread.Sleep(1000);                }            }        }        static void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)        {            //当进度改变时在新线程中调用该方法,可直接操作控件            Console.Write("Thread ID:{0}-->Progress:{1}/n", System.Threading.Thread.CurrentThread.ManagedThreadId, e.ProgressPercentage);        }        static void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)        {            //当DoWork方法处理完成后(不论是否为取消)在新线程中调用此方法            Console.Write("Thread ID:{0}-->Completed!", System.Threading.Thread.CurrentThread.ManagedThreadId);        }    }}

TAP(基于任务的异步模式):一般使用Task类来实现基于任务的异步模式编程,实现步骤如下:

  1. 通过调用Task类指定的构造函数来实例化Task对象,如:Task task = new Task(Action委托实例,CancellationToken实例);注意:Task类有许多重载的构造函数,可依据实际情况进行调用
  2. 调用Task实例对象的Start方法启动异步执行构造函数参数中Action委托实例所引用的方法,如:task.Start()

可参见这篇博文:http://www.VEVb.com/heyuquan/archive/2013/04/18/Task-based-Asynchronous-Pattern.html

具体的使用代码示例如下(为了让新手也能看懂,所以此处我采用标准的委托实例方法来写,大家可以使用Lambda表达式来写,那样代码量就会精简一些):

using System;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace WindowsFormsApplication1{    public partial class Form4 : Form    {        CancellationTokenSource cts = new CancellationTokenSource(); //用于发出与接收任务取消信息        public Form4()        {            InitializeComponent();        }        private void Form4_Load(object sender, EventArgs e)        {            TestTAP();        }        private void TestTAP()        {            WriteMsg(string.Format( "Main Thread ID:{0}", Thread.CurrentThread.ManagedThreadId));            SynchronizationContext syncContext = SynchronizationContext.Current; //获取当前同步上下文,用于在异步中可操作UI上的控件等            Task task = new Task(new Action<object>(TAPAsyncMethod), syncContext);            task.Start();        }        private void TAPAsyncMethod(o
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表