栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

.Net Core异步编程

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

.Net Core异步编程

.Net Core异步编程 一. 用async关键字修饰的方法
  1. 异步方法的返回值一般是Task,T是真正的返回值类型,Task.惯例:异步方法名字最好以Async结尾,这样方便我们只要是async
  2. 即使方法没有返回值,也最好吧返回值声明为非泛型的Task.
  3. 调用泛型方法时,一般在方法前加上await关键字,这样拿到的返回值就是泛型指定的T类型;
  4. 异步方法的"传染性": 一个方法中如果await调用,则这个方法也必须修饰为async
static async Task Main(string[] args)
{
	string fileName = "d:/1.txt";
    File.Delete(fileName);
    File.WriteAllTextAsync(fileName,"hello async");
    string s = await File.ReadAllTextAsync(fileName);
    Console.WriteLine(s);
}
static async Task Main(string[] args)
{
    
    string fileName = @"F:a1.txt";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10000; i++)
    {
        sb.AppendLine("hello");
    }
    //这里如果不加await,会显示异常 因为还没写入完成就开始读取,读取是独占的
    await File.WriteAllTextAsync(fileName, sb.ToString());

    string s = await File.ReadAllTextAsync(fileName);

    //Task t = File.ReadAllTextAsync(fileName);
    //string s = await t;
    Console.WriteLine(s);
}

如果同样的功能,既有同步方法,又有异步方法,那么首先使用异步方法. .Net5中,很多框架的方法也都支持异步:Main,WinForm时间处理函数.

对于不支持异步方法该怎么办?

Wait()(无返回值); Result(有返回值) 风险:死锁,尽量不用

static async Task Main(string[] args)
{
    int l = await DownloadHtmlAsync("https://www.youzack.com", @"F:a1.txt");
    Console.WriteLine("ok" + l);
}



static async Task DownloadHtmlAsync(string url, string fileame)
{
    using (HttpClient httpClient = new HttpClient())
    {
        string html = await httpClient.GetStringAsync(url);
        await File.WriteAllTextAsync(fileame, html);
        return html.Length;
    }
}
二. await关键字修饰的方法

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

Thread.CurrentThread.ManageThreadId 获得当前线程Id

验证:在耗时异步(写入大字符串)操作前后分别打印线程Id
static async Task Main(string[] args)
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 1000; i++)
    {
        sb.Append("XXXXXXXXXXXXXXXXX");
    }

    await File.WriteAllTextAsync(@"F:a1.txt", sb.ToString());
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

优化:到要等待的时候,如果发现已经执行结束了,那就没必要再切换线程了,剩下的代码就继续在之前的线程上就行执行了

三.异步方法不等于多线程
static async Task Main(string[] args)
{
    Console.WriteLine("之前" + Thread.CurrentThread.ManagedThreadId);
    double r = await CalcAsync(5000);
    Console.WriteLine($"r = {r}");
    Console.WriteLine("之后" + Thread.CurrentThread.ManagedThreadId);
}

public static async Task CalcAsync(int n)
{
    Console.WriteLine("CalcAsync," + Thread.CurrentThread.ManagedThreadId);
    double result = 0;
    Random rand = new Random();
    for (int i = 0; i < n*n; i++)
    {
        result += rand.NextDouble();
    }
    return result;
}
结论: 异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行.

把要执行的代码以委托的形式传递给Task().Run().这样就会从线程池中出去一个线程执行我们的委托

await Task.Run(()=>{
    //耗时操作代码,可以返回return返回值
})
四. 没有async的异步方法

async方法缺点:

  1. 异步方法会生成一个类,运行效率没有普通方法高
  2. 可能占用非常多的线程
 static async Task Main(string[] args)
 {
     string s = await ReadAysnc(1);
     Console.WriteLine(s);
 }



static Task ReadAysnc(int num)
{
    if (num == 1)
    {
        return File.ReadAllTextAsync(@"F:a1.txt");
    }
    else if (num == 2)
    {
        return File.ReadAllTextAsync(@"F:a2.txt");
    }
    else
    {
        throw new ArgumentException();
    }
}

从上面代码分析来看,只甩手Task,不"拆完了再装"反编译上面的代码: 只是普通方法调用

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

返回值为Task的不一定都要标注async,标注async只是让我们可以更方便的await而已

如果一个异步方法只是对别的异步方法调用的转发,并没有太多复杂的逻辑(比如等待A的结果,再调用B;把A调用的返回值拿到内部做一些处理再返回),那么就可以去掉async关键字.

五.暂停调用方法使用

如果想在异步方法中暂停一段时间,不要用Thread.Sleep(),因为它会阻塞调用线程,而要用await Task.Delay().

举例:下载一个网址,3秒后下载另一个

namespace 异步休息
{
    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.youzack.com");
                textBox1.Text = s1.Substring(0, 100);
                //Thread.Sleep(3000);
                await Task.Delay(3000);
                string s2 = await httpClient.GetStringAsync("https://www.baidu.com");
                textBox1.Text = s2.Substring(0, 100);
            }
        }
    }
}

在控制台中没看到区别,但是放到WinForm程序中就看到区别了,ASP.NET Core中也看到区别,但是Sleep()会降低并发.

六.CancellationToken参数

有时需要提前终止任务,比如:请求超时,用户取消请求.

很多异步方法都有CancellationToken参数,用户获得提前终止执行的信号.

CancellationToken结构体 none:空 bool isCancellationRequested 是否取消 (*)Register(Action callback) 注册取消监听 ThrowCancellationRequested() 如果任务被取消,执行到这句话就抛异常
CancellationTokenSource CancelAfter()超时后发出取消信号 Cancel()发出取消信号 CancellationToken Token

为"下载一个网址N次"的方法增加取消功能,分别用GetStringAsync + isCancellationRequested,GetStringAsync + ThrowIfCancellationRequested(),带 CancellationToken的GetStringAsync ()分别实现.取消分用超时,用户敲按键(不能await)实现.

class Program
{
    static async Task Main1(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            cts.CancelAfter(5000);
            await Download2sAsync("https://www.baidu.com", 100, cts.Token);
            while (Console.ReadLine() != "q")
            {

            }
            cts.Cancel();
            Console.ReadLine();
        }
    static async Task Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        cts.CancelAfter(5000);
        await Download2sAsync("https://www.baidu.com", 100,cts.Token);
    }

    

    static async Task Download2sAsync(string url, int n,CancellationToken cancellationToken)
    {
        using (HttpClient httpClient = new HttpClient())
        {
            for (int i = 0; i < n; i++)
            {
                string html = await httpClient.GetStringAsync(url);
                Console.WriteLine($"{DateTime.Now}:{html}");
                await Task.Delay(500);
                if (cancellationToken.IsCancellationRequested)
                {
                    Console.WriteLine("请求被取消");
                    break;
                }
            }
        }
    }
}

ASP.NET Core开发中,一般不需要自己处理CancellationToken,CancellationTokenSource这些,只要做到"能转发CancellationToken就转发即可".ASP.NET Core会对于用户请求中断进行处理.

七.Task类的重要方法
  1. Task WhenAny(IEnumerable tasks)等,任何一个Task完成,Task就完成
  2. Task WhenAll(params Task[] tasks)等,所有Task完成,Task才完成,用于等待多个任务执行结束,但是不在乎他们的执行顺序.
  3. FromResult()创建普通数值的Task对象.
八.其他问题 1.接口中的异步方法:

async是提示编译器为异步方法中的await代码进行分段处理的,而一个异步方法是否修饰了async对于方法的调用者来讲没区别的,因此对于接口中的方法或者抽象方法不能修饰为async

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }

    interface ITest
    {
        Task GetCharCount(string file);
    }

    class Test : ITest
    {
        public async Task GetCharCount(string file)
        {
            string s = await File.ReadAllTextAsync(file);
            return s.Length;
        }
    }
}
2.异步与yield

回顾:yield return不仅能够简化数据的返回,而且可以让数据处理"流水线化",提升性能

static IEnumerable Test1()
{
    List list = new List();
    list.Add("hello1");
    list.Add("hello2");
    list.Add("hello3");
    return list;
}

static IEnumerable Test2()
{
    yield return "hello1";
    yield return "hello2";
    yield return "hello3";
}

效果是一样的.

在旧版C#中,async方法中不能用yield.从C# 8.0开始,吧返回值声明为IAsyncEnumerable(不要带Task),然后遍历的时候await foreach()即可

static async Task Main(string[] args)
{
    await foreach (var s in Test3())
    {
        Console.WriteLine(s);
    }
}
static async IAsyncEnumerable Test3()
{
    yield return "hello1";
    yield return "hello2";
    yield return "hello3";
}

ASP.NET Core和控制台项目中没有SynchronizationContext,因此不用管ConfigureAwait(false)等.不要同步,异步混用

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1023856.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号