c# 高并发的几种方式(二)

341 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情 

上一章中,我们主要介绍了一种分布式模型actor来处理并发。实际中,处理并发的方式有很多种,异步、并行、TPL数据流,响应式等等。本章节主要记录&学习一下异步编程。

异步编程

异步编程首先比较常见的应用场景是GUI里,界面刷新和数据处理是异步操作。实际我们可以把异步编程简单的分为两类:GUI,Server

  • 对于GUI程序,异步编程提高了响应能力,很好的解决了界面假死的状态,异步编程可以在程序执行处理计算任务的同时响应用户的操作。
  • 对于server应用,异步编程实现了可扩展性,服务端开启线程池满足可扩展性,使用异步编程以后,通常扩展性可以上一个台阶。

C#异步编程

简要

async和await关键字是实现异步编程的关键,两者要成对出现。

  • async 用在函数的声明上,他的主要目的是是方法内的await生效。如果async有返回值,应该返回TASk类型,如果没有返回值需要接收,则返回TASK,用来在异步方法结束的时候通知主进程task的状态。
  • await 用在函数的内部,如果函数的声明中包含async,而函数内部没有使用await关键字,编译就会报错。
  • 一个异步方法里面,可以包含多个await,当程序在await处进行异步等待的时候,会保存当前的运行上下文。
  • 如果当前的context不为空,这个上下文就是当前的synchronizationcontext
  • 如果当前的context为空,这个上下文就是当前的taskscheduler
  • 默认情况下,运行 UI 线程时采用 UI 上下文,处理 ASP .NET 请求时采用 ASP .NET 请求上下文
  • 其他很多情况下则采用线程池上下文。

丐版demo

新建一个winfrom程序

程序中,我们在页面上放置两个按钮,两个显示框。
实现简单的功能,点击按钮1,textbox显示hello,点击按钮2,textbox2显示world

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormsAsync
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// button1回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            this.textBox1.Text = "Hello";
        }
        /// <summary>
        /// button2回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            this.textBox2.Text = "world";
        }
    }
}

image.png

修改code

我们修改button1的回调函数,在函数中增加耗时任务。简单的增加一个sleep好了。

        /// <summary>
        /// button1回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            this.textBox1.Text = "Hello";
            for (int loop = 0; loop < 10; loop++)
            {
                Thread.Sleep(1000);
            }
        }

我们再运行程序,点击button1后,界面不会刷新,并且在sleep期间,界面也不会响应button2的动作。好了,我的hello world被搞坏了,这就是所谓的界面假死状态。想一想,如果给用户的体验是这个样子,岂不是用户天天要抓狂。。。

异步

我们使用异步方法,把上面的耗时任务异步处理,使用async和awiait优化code如下;

        /// <summary>
        /// 异步耗时任务
        /// </summary>
        private async void BackTaskAsync()
        {
            await Task.Run(() =>
            {
                for (int loop = 0; loop < 10; loop++)
                {
                    Thread.Sleep(1000);
                }
                MessageBox.Show("Async Over");
            });
        }
        /// <summary>
        /// button1回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            this.textBox1.Text = "Hello";
            BackTaskAsync();
        }

这次,我们再点击button1,hello可以立刻显示,并且同时button2也不受影响的进行操作。奈斯,我的hello world又回来了。
在我们操作一番后,异步的task接收messagebox才会弹出。

异步更新

我们再次修改code,让异步的code耗时任务更新界面UI,前面我们介绍过,异步的gui中,获取的上下文是gui上下文,所以我们可以异步的去更新gui。

        /// <summary>
        /// 异步耗时任务
        /// </summary>
        private async void BackTaskAsync()
        {
            var res = await Task.Run(() =>
            {
                for (int loop = 0; loop < 10; loop++)
                {
                    Thread.Sleep(1000);
                }
                return "Task Async Over";
            });
            this.textBox1.Text = res;
        }
        /// <summary>
        /// button1回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            this.textBox1.Text = "Hello";
            BackTaskAsync();
        }

以上代码的执行结果是,button1先显示hello,最后,异步执行完毕后,textbox1变成task async over。

image.png

这就是丐版的异步demo。
关于异步的其他高级用法和场景大家可以继续发掘研究、这里仅浅谈一下。