C#之:异步编程 -1

异步编程简介

异步编程有两大好处。第一个好处是对于面向终端用户的GUI 程序:异步编程提高了响应能力。我们都遇到过在运行时会临时锁定界面的程序,异步编程可以使程序在执行任务时仍能响应用户的输入。第二个好处是对于服务器端应用:异步编程实现了可扩展性。服务器应用可以利用线程池满足其可扩展性,使用异步编程后,可扩展性通常可以提高一个数量级。

.NET中有三种异步编程的模型接口:

  1. EAP 是Event-based Asynchronous Pattern(基于事件的异步模型)的简写。
  2. APM(Asynchronous Programming Model)是.Net旧版本中广泛使用的异步编程模型。
  3. TPL(Task Parallel Library)是.Net 4.0之后带来的新特性,更简洁,更方便。现在在.Net平台下已经大面积使用。(重点)

1- EAP模式:

示例:

		private void button1_Click(object sender, EventArgs e)
        {
            WebClient wc = new WebClient();
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            wc.DownloadStringCompleted += Wc_DownloadStringCompleted;
            wc.DownloadStringAsync(new Uri("https://github.com/github"));
        }  

		 private void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            textBox1.Text = e.Result;
        }

说明:某个对象的方法中有以 Async 结尾。并且有异步完成事件,两个条件缺一不可。如上面代码 DownloadStringAsync 是异步下载方法。 wc.DownloadStringCompleted += Wc_DownloadStringCompleted; 注册了异步完成事件,等待异步执行完毕后,自动触发事件响应。

EAP的类的特点是:一个异步方法配一个***Completed事件。.Net中基于EAP的类比较少。也有更好的替代品,因此了解即可。

2- APM模式:

使用了APM的异步方法会返回一个IAsyncResult对象,这个对象有一个重要的属性AsyncWaitHandle,他是一个用来等待异步任务执行结束的一个同步信号。

示例:

 private void button1_Click(object sender, EventArgs e)
        {
            using (FileStream fs = new FileStream("D:/1.txt", FileMode.OpenOrCreate, FileAccess.Read))
            {
                byte[] buffer = new byte[1024];
                //fs.BeginRead():开始异步方法,开始读取文件
                IAsyncResult aResult = fs.BeginRead(buffer, 0, buffer.Length, null, null);
                aResult.AsyncWaitHandle.WaitOne(); //等待任务执行结束,一定要等待读取完成,否则没有内容
                string s = Encoding.Default.GetString(buffer);
                // fs.EndRead() 结束异步方法
                fs.EndRead(aResult);
                this.textBox1.Text = s; 
            }
        }

如果不加aResult.AsyncWaitHandle.WaitOne() 那么很有可能打印出空白,因为BeginRead只是“开始读取”。调用完成一般要调用EndXXX来回收资源。
APM的特点是:方法名字以BeginXXX开头,返回类型为IAsyncResult,调用结束后需要EndXXX。
.Net中有如下的常用类支持APM:Stream、SqlCommand、Socket等。

3-TPL模式:

TPL模式是现在主流的异步模式。
现代的异步.NET 程序使用两个关键字:async 和await。async 关键字加在方法声明上,它的主要目的是使方法内的await 关键字生效(为了保持向后兼容,同时引入了这两个关键字)。如果async 方法有返回值,应返回Task;如果没有返回值,应返回Task。这些task 类型相当于future,用来在异步方法结束时通知主程序。
注意:
不要用void 作为async 方法的返回类型! async 方法可以返回void,但是这仅限于编写事件处理程序。一个普通的async 方法如果没有返回值,要返回Task,而不是void。

示例:

 		private async void button2_Click(object sender, EventArgs e)
        {
            //TPL经典写法

            //注意方法中如果有await,则方法必须标记为async,不是所有方法都可以被轻松的标记为async。WinForm中的事件处理方法都可以标记为async、MVC中的Action方法也可以标记为async、控制台的Main方法不能标记为async

            //TPL的特点是:方法都以XXXAsync结尾,返回值类型是泛型的Task<T>。
            using (FileStream fs = File.OpenRead("D:/2.txt"))
            {
                byte[] buffer = new byte[512];
                //await:等待ReadAsync执行结束
                await fs.ReadAsync(buffer, 0, buffer.Length);
                textBox1.Text = Encoding.Default.GetString(buffer);
            }
        }

注意点:
如果有await,则方法必须标记为async,不是所有方法都可以被轻松的标记为async。WinForm中的事件处理方法都可以标记为async、MVC中的Action方法也可以标记为async、控制台的Main方法不能标记为async。

什么样的方法能用呢? 方法的返回值必须是 Task< T > 类型的。

示例:

 		private async void button3_Click(object sender, EventArgs e)
        {
            WebClient wc = new WebClient();
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            string s = await wc.DownloadStringTaskAsync(new Uri("http://www.rupeng.com"));
            string s1 = await wc.DownloadStringTaskAsync(new Uri("http://www.github.com"));
            textBox1.Text = s1;
            textBox2.Text = s;
            //虽然没有在单独线程中执行但是并不会导致页面卡顿
        }

async 方法在开始时以同步方式执行。在async 方法内部,await 关键字对它的参数执行一个异步等待。它首先检查操作是否已经完成,如果完成了,就继续运行(同步方式)。否则,它会暂停async 方法,并返回,留下一个未完成的task。一段时间后,操作完成,async 方法就恢复运行。

异步方法编写方式:
示例:
返回值为Task,潜规则(不要求)是方法名字以Async结尾:

 		private Task<string> TestAsync()
        {
            return Task.Run(() =>
            {
                Thread.Sleep(3000);
                return "你好!";
            });
        }

 		/// <summary>
        /// 调用异步方法的方法必须标记 async
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void Button7_Click(object sender, EventArgs e)
        {
            string s = await TestAsync();
            MessageBox.Show(s);
        }

实例:

在程序用调用Http接口、请求http资源。
使用 HttpClient 类:
HttpClient发出Get请求获取文本响应:string html = await hc.GetStringAsync(“http://www.baidu.com”);
HttpClient发出Post请求使用Task< HttpResponseMessage > PostAsync(string requestUri, HttpContent content) 方法,第一个参数是请求的地址,第二个参数就是用来设置请求内容的。HttpContent是抽象类,主要的子类有FormUrlEncodedContent(表单格式请求)、StringContent(字符串请求)、MultipartFormDataContent(Multipart表单请求,一般带上传文件信息)、StreamContent(流内容)。

表单提交

		/// <summary>
        ///表单提交
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void button4_Click(object sender, EventArgs e)
        {
            HttpClient hc = new HttpClient();
            //提交Form表单请求(post)
            //表单参数写法1
            List<KeyValuePair<string, string>> paramters = new List<KeyValuePair<string, string>>();
            paramters.Add(new KeyValuePair<string, string>("userName", "admin"));
            paramters.Add(new KeyValuePair<string, string>("password", "123"));
            //表单参数写法2
            //Dictionary<string, string> keyValues = new Dictionary<string, string>();
            //keyValues["userName"] = "admin";
            //keyValues["password"] = "123";

            FormUrlEncodedContent content = new FormUrlEncodedContent(paramters);//接受KeyValue对参数

           HttpResponseMessage msg=await  hc.PostAsync("http://127.0.0.1:8011/Home/Login",content);

            //HttpContent con = msg.Content; //返回报文体(包含字符串,流,等)
            // msg.Headers //返回响应头,返回System.Net.Http.Headers.HttpResponseHeaders。HTTP响应的集合
            // msg.StatusCode//返回响应码
            //等等
            MessageBox.Show("响应码:" + msg.StatusCode);
            string html = await msg.Content.ReadAsStringAsync();
            MessageBox.Show(html);
        }

上传文件

/// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void button6_Click(object sender, EventArgs e)
        {
            HttpClient hc = new System.Net.Http.HttpClient();
            //提供使用多部分/表单数据MIME类型编码的内容的容器
            MultipartFormDataContent content = new MultipartFormDataContent();
            //添加报文头内容
            content.Headers.Add("UserName", "admin");
            content.Headers.Add("Password", "123");
            using (FileStream fs = File.OpenRead("C:/Users/wang/Desktop/errorImg.png"))
            {
                //file 名字必须和MVC方法参数一样
                content.Add(new StreamContent(fs), "file", "1.png");
                HttpResponseMessage msg = await hc.PostAsync("http://127.0.0.1:8011/Home/Upload", content);
                MessageBox.Show("状态码:" + msg.StatusCode);
                string html = await msg.Content.ReadAsStringAsync();
                MessageBox.Show("返回报文体:" + html);
            }
        }

发出POST请求,请求体是 JSON格式

 private async void button5_Click(object sender, EventArgs e)
        {
            HttpClient hc = new HttpClient();
            string json = "{userName:'admin',password:'123'}";
            StringContent content = new StringContent(json); //报文体
            //contentype必不可少
            content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

            //HttpResponseMessage:表示包含状态码和数据的HTTP响应消息。
            HttpResponseMessage msg =await hc.PostAsync("http://127.0.0.1:8011/Home/Login2", content);
            MessageBox.Show("状态码:" + msg.StatusCode);
            string html = await msg.Content.ReadAsStringAsync();
            MessageBox.Show("返回报文体:" + html);

        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值