学习.NET core笔记第三章——异步编程(C#)

本节内容大概分为 async背后的线程切换、异步方法不等于多线程、为什么有的异步方法没有标async、sleep()方法

async背后的线程切换

await调用的等待期间,.NET框架会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池再取出一个线程执行后续代码

这里有一个例子来帮助大家理解

先用Thread .CurrentThread.ManagedThreadId来获取当前的线程ID,然后我们进行一个将字符串写入文件的异步方法,再用Thread .CurrentThread.ManagedThreadId来查看当前进程的ID

using System.Text;
//Thread .CurrentThread.ManagedThreadId获取当前线程的ID
Console.WriteLine(Thread .CurrentThread.ManagedThreadId);
StringBuilder sb = new StringBuilder();
//如果执行次数过少,线程可能就是一个,取决电脑性能
for (int i =0; i < 1000; i++)
{
    sb.Append("XXXXXXXXXXXXXXXX");
}
await File.WriteAllTextAsync(@"D:\.NET core\验证线程.txt",sb.ToString());
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

从这里我们可以看出前后完成任务的不是同一个线程

学习.NET core笔记第三章——异步编程(C#)_第1张图片

当然如果执行次数过少,所使用的线程可能就是一个,取决电脑性能 

这是一种系统的优化:到要等待的时候,如果发现当前任务以及执行结束,那也就没必要切换线程了,剩下的代码就在之前的线程上继续执行了。(框架自己对其的优化,并不是程序员对其的优化)

就像上面的那个例子把字符串改写为简单的数值,获取到的线程ID就都是1

学习.NET core笔记第三章——异步编程(C#)_第2张图片

 异步方法不等于多线程

异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行。

这是一个验证的例子


namespace 异步方法不等于多线程
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            //这里线程调用堆栈可以看到使用的线程是1,但是没有正常打印出来,使用的是Thread.CurrentThread.ManagedThreadId
            Console.WriteLine(Environment.CurrentManagedThreadId);//使用这个方法后成功打印出来了
            Console.WriteLine("之前:", +Environment.CurrentManagedThreadId);
            double r = await CalcAsync(50);
            Console.WriteLine($"r={r}");
            Console.WriteLine("之前:", +Environment.CurrentManagedThreadId);
        }
        public static async Task CalcAsync(int n)
        {
            /* Console.WriteLine("CalcAsync", +Thread.CurrentThread.ManagedThreadId);
             double result = 0;
             Random random = new Random();
             for (var i = 0; i < n * n; i++)
             {
                 result += random.NextDouble();

             }
             return result;*/
            //run方法,把方法放到一个新的线程里面去
            return await Task.Run(() =>
            {
                Console.WriteLine("CalcAsync", +Environment.CurrentManagedThreadId);
                double result = 0;
                Random random = new Random();
                for (var i = 0; i < n * n; i++)
                {
                    result += random.NextDouble();

                }
                return result;
            });
        }
    }
}

为什么有的异步方法没有标async

首先我们要了解关于async的缺点

  1. 异步方法会产生一个类,运行效率没有普通方法高
  2. 可能会占用非常多的线程
  3. 可能会导致线程调度性能比较低

因此有的方法不写async,直接使用Task,不“拆完了再装”反编译上面的代码,只是普通的方法调用

优点:运行效率高,不会造成线程浪费

(返回值为Task的不一定都要标注async,标准async只是让我们可以更方便的使用await)

什么时候用async?什么时候可以不写async?

如果一个异步方法只是对别的异步方法调用的转发,并没有太多复杂的逻辑(比如等待A的结构,再调用D;把A调用的返回值拿到内部做一些处理在返回),那么就可以去掉async关键字,当然如果调用异步方法的时候对其做了其他的操作,就要加上async

这里是一个简单的对比方法,二者都是读取一个文件

namespace 没有async关键字的异步方法
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            string s = await ReadAsync(1);
            Console.WriteLine(s);
        }
        //传统的异步方法
        /* static async Task ReadAsync(int num)
         {
             if (num == 1)
             {
                 string s = await File.ReadAllTextAsync(@"D:\.NET core\没有async关键字的异步方法\1.txt");
                 return s;
             }
             else if(num==2)

             {
                 string s = await File.ReadAllTextAsync(@"D:\.NET core\没有async关键字的异步方法\2.txt");
                 return s;
             }
             else
             {
                 throw new ArgumentException();
             }
             }*/
        //普通方法调用,运行效率更高
        static  Task ReadAsync(int num)
        {
            if (num == 1)
            {
                return File.ReadAllTextAsync(@"D:\.NET core\没有async关键字的异步方法\1.txt");
                
            }
            else if (num == 2)

            {
                return File.ReadAllTextAsync(@"D:\.NET core\没有async关键字的异步方法\2.txt");
               
            }
            else
            {
                throw new ArgumentException();
            }
        }
    }
    
}

sleep()方法

尽量少去使用sleep()方法

如果想在异步方法中暂停一段时间,不要用Thread.sleep();使用该方法是阻塞调用线程,在这里可能会阻塞主进程,我们可以使用await.Task.Delay()(delay阻塞的是毫秒delay(3000)也就是3s);该方法不会阻塞主线程

这是一个简单的winform实验,用到了一个button控件和一个textbox控件

学习.NET core笔记第三章——异步编程(C#)_第3张图片

button事件代码:

namespace 异步休息_sleep和await区别_
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                string s1 = await httpClient.GetStringAsync("https://www.baidu.com");
                textBox1.Text = s1.Substring(0, 20);
                //  Thread.Sleep(3000);
                //使用sleep方法后进程容易卡死在这里
                await Task.Delay(3000);
                string s2 = await httpClient.GetStringAsync("https://www.youzack.com");
                textBox1.Text = s2.Substring(0, 100);
            }
        }
    }
}

你可能感兴趣的:(学习,.netcore,笔记)