C#基础学习--异步编程

目录

什么是异步

async/await 特性的结构

 什么是异步方法

 异步方法的控制流

await 表达式


什么是异步

启动程序时,系统会在内存中创建一个新的进程。进程是构成运行程序的资源的集合。进程是构成运行程序的资源的集合。这些资源包括虚地址空间,文件句柄和许多其他程序运行所需的东西

在进程内部,系统创建了一个称为线程的内核(kernel)对象,它代表了真正执行的程序(线程是执行进程  的简称)。一旦进程建立,系统会在Main方法的第一行语句处就开始线程的执行

 如果一个程序调用某个方法,并在等待方法执行所有处理之后才继续执行,我们就称这样的方法是同步的。这是默认的形式

async/await 特性的结构

异步的方法在处理完成之前就返回到调用方法   特性由三个部分组成:

 什么是异步方法

异步方法在完成其工作之前返回到调用方法,然后在调用方法继续执行的时候完成其工作

返回类型必须是下面的三种之一:

 任何返回Task<T>类型的异步方法其返回值必须为T类型或可以隐式转换为T的类型

 下面的代码使用了Thread.Sleep 方法来暂停主线程,这样它就不会在异步方法完成之前退出

 异步方法的控制流

异步方法的结构包含三个不同的区域

 当达到await 表达式时,异步方法将控制返回到调用方法。如果方法的返回类型为Task或Task<T>类型,将创建一个Task对象,表示需异步完成的任务和后续,然后将该Task返回到调用方法

await 表达式

await 表达式指定了一个异步执行的任务。语法如下,由await 关键字和一个空闲对象(陈我给任务)组成,这个任务可能是一个Task类型的对象,也可能不是。默认情况下,这个任务在当前线程异步运行

   await   task

代码依次输出 5 6 7 8 

using System;
using System.Threading.Tasks;

namespace Csharpzuoye
{
    static class MyClass
    {
        private static int GetSum(int i1,int i2)
        {
            return i1 + i2;
        }

        public static async Task DoworkAsync()
        {
            int value = await Task.Run(() => GetSum(5, 6));
            Console.WriteLine(value.ToString());
        }
    }

    class program
    {
        static void Main()
        {
            Task t = MyClass.DoworkAsync();
            t.Wait();
            Console.WriteLine("Press Enter Key to exit");
            Console.Read();
        }
    }
}

Lambda 函数() => GetSum(5,6) 满足 Func<TRsult> 委托,因为它没有参数,且返回单一的值。

取消一个异步操作

可以在自己的异步方法中加入这个特性,System.Threading.Task 命名空间中有两个类是为此目的而设计的:CancellationToken 和 CancellationTokenSource

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Csharpzuoye
{
    class Program
    {
        static void Main()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken token = cts.Token;

            MyClass mc = new MyClass();
            Task t = mc.RunAsync(token);

            //Thread.Sleep(3000);  //等待3秒
            //cts.Cancel();  //取消操作

            t.Wait();
            Console.WriteLine($"Was Cancelled : {token.IsCancellationRequested}");
        }
    }

    class MyClass
    {
        public async Task RunAsync(CancellationToken ct)
        {
            if (ct.IsCancellationRequested)
            {
                return;
            }
            await Task.Run(() => CycleMethod(ct), ct);
        }

        void CycleMethod(CancellationToken ct)
        {
            Console.WriteLine("Starting CycleMethod");
            const int max = 5;
            for (int i = 0; i < max; i++)
            {
                if (ct.IsCancellationRequested)  //监控 CancellationToken
                    return;
                Thread.Sleep(1000);
                Console.WriteLine($"{i + 1} of {max} iterations completed");
            }
        }
    }

}

 取消掉这里的注释后得到以下结果:

异常处理和 await 表达式

可以像使用其他表达式那样,将 await 表达式放在 try 语句内,try ... catch ... finally 结构将按你期望的那样工作

在调用方法中同步地等待任务

调用方法可以调用任意多个异步方法并接收它们返回的 Task 对象。然后代码继续执行其他任务,但在某个节点可能会需要等待某个特殊Task 对象完成,然后再继续。为此,Task 类提供了一个实例方法 Wait ,可以在 Task 对象上调用这个方法

在下面的代码中,调用方法 DoRun 调用异步方法 CountCharacterAsync 并接收其返回的 Task<int> ,然后调用 Task 实例的 Wait 方法,等待任务 Task 结束。等结束时再显示结果信息

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;

namespace Csharpzuoye
{
    static class MyDownLoadString
    {
        public static void DoRun()
        {
            Task<int> t = CountCharacterAsync("http://www.illustratedcsharp.com");
            //等待任务 t 结束
            t.Wait();
            Console.WriteLine($"The task has finished,returning value {t.Result}");
        }

        private static async Task<int> CountCharacterAsync(string site)
        {
            string result = await new WebClient().DownloadStringTaskAsync(new Uri(site));
            return result.Length;
        }
    }

    class Program
    {
        static void Main()
        {
            MyDownLoadString.DoRun();
        }
    }

}

 重载方法

在异步方法中异步地等待任务

有时在异步方法中,会希望用 await 表达式来等待 Task,这时异步方法会返回到调用方法,但该异步方法会等待一个或所有任务完成。可以通过 Task.WhenAll 和 Task.WhenAll 方法来实现。这两种方法称为组合子

下面的代码展示了一个使用 Task.WhenAll 方法的示例。该方法异步地等待所有与之相关的 Task 完成,不会占用主线程的时间。注意,await 表达式的任务就是调用 Task.WhenAll

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Generic;

namespace Csharpzuoye
{
    class MyDownloadString
    {
        public void DoRun()
        {
            Task<int> t = CountCharacterAsync("http://www.microsoft.com", 
                "http://www.illustratedcsharp.com");
            Console.WriteLine("DoRun: Task {0} Finished", t.IsCompleted ? "" : "Not");
            Console.WriteLine("DoRun: Result = {0}", t.Result);
        }

        private async Task<int> CountCharacterAsync(string site1,string site2)
        {
            WebClient wc1 = new WebClient();
            WebClient wc2 = new WebClient();
            Task<string> t1 = wc1.DownloadStringTaskAsync(new Uri(site1));
            Task<string> t2 = wc2.DownloadStringTaskAsync(new Uri(site2));

            List<Task<string>> tasks = new List<Task<string>>();
            tasks.Add(t1);
            tasks.Add(t2);

            await Task.WhenAll(tasks);

            Console.WriteLine("    CCA: T1{0}Finished", t1.IsCompleted ? "" : "Not");
            Console.WriteLine("    CCA: T2{0}Finished", t2.IsCompleted ? "" : "Not");

            return t1.IsCompleted ? t1.Result.Length : t2.Result.Length;
        }

    }

    class Program
    {
        static void Main()
        {
            MyDownloadString ds = new MyDownloadString();
            ds.DoRun();
        }
    }

}

Task.Delay 方法

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Generic;
using System.Diagnostics;

namespace Csharpzuoye
{
    class Simple
    {
        Stopwatch sw = new Stopwatch();

        public void DoRun()
        {
            Console.WriteLine("Caller: Before call");
            ShowDelayAsync();
            Console.WriteLine("Caller: After call");
        }

        private async void ShowDelayAsync()
        {
            sw.Start();
            Console.WriteLine($"   Before Delay: {sw.ElapsedMilliseconds}");
            await Task.Delay(1000);
            Console.WriteLine($"   After Delay: {sw.ElapsedMilliseconds}");
        }
    }

    class Program
    {
        static void Main()
        {
            Simple ds = new Simple();
            ds.DoRun();
            Console.Read();
        }
    }

}

重载方法

GUI 程序中的异步操作

Task.yield

Task.yield 方法创建一个立即返回的 awaitable 。等待一个 yield 可以让异步方法在执行后续部分的同时返回到调用方法。可以将其理解成离开当前的消息队列,回到队列末尾,让处理器有时间处理其他任务

每次执行 yield 方法,线程中的其他任务就得以执行

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Generic;
using System.Diagnostics;

namespace Csharpzuoye
{
    class DoStuff
    {
        public static async Task<int> FindSeriesSum(int i1)
        {
            int sum = 0;
            for (int i = 0; i < i1; i++)
            {
                sum += i;
                if (i % 1000 == 0)
                { 
                    await Task.Yield();
                }
            }
            return sum;
        }
    }

    class Program
    {
        static void Main()
        {
            Task<int> value = DoStuff.FindSeriesSum(1_000_0000);
            CountBig(1000_000);
            CountBig(1000_000);
            CountBig(1000_000);
            CountBig(1000_000);
            Console.WriteLine($"Sum :{value.Result}");
        }

        private static void CountBig(int p)
        {
            for (int i = 0; i < p; i++) ;
            Console.WriteLine("1");
        }
    }

}

Yield 方法在 GUI 程序中非常有用,可以中断大量工作,让其他任务使用处理器

使用异步 Lambda 表达式

BackgroundWorker 类

并行循环

Parallel.For 循环和 Parallel.Foreach 循环。这两个结构位于 System.Threading.Tasks 命名空间中

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Generic;
using System.Diagnostics;

namespace Csharpzuoye
{
    class DoStuff
    {
        static void Main()
        {
            Parallel.For(0, 15, i =>
                Console.WriteLine($"The square of {i} is {i * i}"));
        }
    }

}

其他异步编程模式

BeginInvoke 和 EndInvoke

等待直到完成模式

在这种模式里,原始线程发起一个异步方法,做一些其他处理,然后停止并等待,直到开启的线程结束。

IAsyncResult iar = del.BeginInvoke(3,5,null,null);

     //在发起线程中异步执行方法的同时

     //在调用线程中处理一些其他事情

long result = del.EndInvoke(iar);

完整示例:

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Collections.Generic;
using System.Diagnostics;

namespace Csharpzuoye
{
    delegate long MyDel(int first, int second);  //声明委托类型

    class Program
    {
       static long Sum(int x,int y)  //声明异步方法
       {
            Console.WriteLine("           Inside Sum");
            Thread.Sleep(100);

            return x + y;
       }

        static void Main()
        {
            MyDel del = new MyDel(Sum);

            Console.WriteLine("Before BeginInvoke");
            IAsyncResult iar = del.BeginInvoke(3, 5, null, null);  //开始异步调用
            Console.WriteLine("After BeginInvoke");

            Console.WriteLine("Doing stuff");

            long result = del.EndInvoke(iar);  //等待结束并获取结果
            Console.WriteLine($"After EndInvoke:{result}");
        }
    }

}

这是旧版的结果,现在已经不支持 begininvoke 了,代码运行不了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值