目录
什么是异步
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}");
}
}
}
文章来源:https://www.toymoban.com/news/detail-425220.html
这是旧版的结果,现在已经不支持 begininvoke 了,代码运行不了文章来源地址https://www.toymoban.com/news/detail-425220.html
到了这里,关于C#基础学习--异步编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!