C# Async异步
原文链接:https://blog.csdn.net/zuheyawen/article/details/99863588
转载连接:https://www.cnblogs.com/wcrBlog/p/11690460.html
前言
C#异步编程有几种实现方式,异步方法就是其中的一种。异步方法是 C#5.0 才有的新特性,主要采用 async、await 关键字声明为异步方法,完成对方法的异步调用。C#5.0 对应的 VS 版本是 VS2012,对应的 .NET Framework 版本是 v4.5,所以需要在此基础上才支持。(否则可能报:找不到“async”修饰符所需的所有类型。目标框架版本是否不正确,或者缺少对程序集的引用?)
什么是异步方法
1. 异步方法,是指在执行当前方法的同时,可以异步的去调用其他方法(异步方法),并且不会阻塞当前方法的线程。
2. 使用了 async 修饰符的方法称为异步方法,通常配合 await 运算符和 Task 异步任务一起使用。
1) 如果方法使用了 async 修饰符,则方法中需要包含一个以上 await 运算符,否则将以同步执行。
2) 反之,如果方法中包含一个以上 await 运算符,则必须声明为一个异步方法,即使用 async 修饰符。
3. Task 分为两种:
1) Task,表示可以执行一个异步操作,声明如下:
public class Task : IAsyncResult, IDisposable { }
2) Task<TResult>,表示可以执行带有返回值的异步操作,声明如下:
public class Task<TResult> : Task { }
4. 异步方法的返回类型必须为 void、Task、Task<TResult> 中的其中一种。
1) void,表示无返回值,不关心异步方法执行后的结果,一般用于仅仅执行某一项任务,但是不关心结果的场景。
2) Task,表示异步方法将返回一个 Task 对象,该对象通常用于判断异步任务是否已经完成,可以使用 taskObj.Wait() 方法等待,或者 taskObj.IsCompleted 判断。
3) Task<TResult>,表示异步方法将返回一个 Task<TResult> 对象,该对象的 Result 属性则是异步方法的执行结果,调用该属性时将阻塞当前线程(异步方法未执行完成时)。
归纳一下:void 不关心结果;Task 只关心是否执行完成;Task<TResult> 不止关心是否执行完成,还要获取执行结果。
下面通过几个生活中比较形象的例子来理解异步方法的使用
// See https://aka.ms/new-console-template for more information
//Async.DropLitter();
//Async.OpenMainsSwitch();
//Async.CookDinner();
//Async.AsyncBuyGoods();
//Async.CookDinner_CancelBuySalt();
Async.CookDinner_MultiCancelBuySalt();
public static class Async
{
#region 扔垃圾为例 不关心结果, 返回void类型
/// <summary>
/// 扔垃圾
/// </summary>
public static void DropLitter()
{
Console.WriteLine("老婆开始打扫房间");
Console.WriteLine("垃圾满了,快起扔垃圾");
CommandDropLitter();
Console.WriteLine("不管他继续打扫");
Thread.Sleep(1000);
Console.WriteLine("老婆把房间打扫好了");
}
/// <summary>
/// 通知我去扔垃圾
/// </summary>
public static async void CommandDropLitter()
{
Console.WriteLine("这时我准备去扔垃圾");
await Task.Run(() =>
{
Console.WriteLine("屁颠屁颠去扔垃圾");
Thread.Sleep(1000);
});
Console.WriteLine("垃圾扔了还有啥吩咐");
}
#endregion
#region 看电视为例 关心是否执行完成,返回Task类型
public static void OpenMainsSwitch()
{
Console.WriteLine("我和老婆正在看电视");
Console.WriteLine("突然停电,快去看下是不是跳闸了");
Task task = CommandOpenMainSwitch();
Console.WriteLine("没电了先玩会手机吧");
//Thread.Sleep(100);
Console.WriteLine("手机也没电了只能等电源打开");
while (!task.IsCompleted) { Thread.Sleep(100); }
Console.WriteLine("又有电了我们继续看电视");
}
public static async Task CommandOpenMainSwitch()
{
Console.WriteLine("这时我准备去打开电源开关");
await Task.Run(() => {
Console.WriteLine("屁颠屁颠的去打开电源开关");
//Thread.Sleep(1000);
});
Console.WriteLine("电源开关打开了");
}
#endregion
#region 买盐 不止关心是否执行完成,还要获取执行结果。返回 Task<TResult> 类型
public static void CookDinner()
{
Console.WriteLine("老婆开始做饭");
Console.WriteLine("哎呀,没盐了");
Task<string> task = CommandBuySalt();
Console.WriteLine("不管他继续炒菜");
//Thread.Sleep(100);
string result = task.Result;
Console.WriteLine("用了盐炒的菜就是好吃【{0}】", result);
Console.WriteLine("老婆把饭做好了");
}
public static async Task<string> CommandBuySalt()
{
Console.WriteLine("这时我准备去买盐了");
string result = await Task.Run(() =>
{
Console.WriteLine("屁颠屁颠的去买盐");
//Thread.Sleep(1000);
return "盐买回来了,顺便我还买了一包烟";
});
Console.WriteLine(result);
return result;
}
#endregion
#region 买盐买烟买酒 异步方法中开启多个Task
public static void AsyncBuyGoods()
{
Console.WriteLine("老婆开始做饭");
Console.WriteLine("哎呀,没盐了");
Task task = BuyGoods();
Console.WriteLine("不管他继续炒菜");
task.Wait();
Console.WriteLine("老婆把饭做好了");
Console.WriteLine("抽着烟,喝着酒,吃着用了盐炒的菜,生活乐无边");
}
public static async Task BuyGoods()
{
Console.WriteLine("这时我准备去买盐了");
await Task.Factory.StartNew((state) =>
{
Console.WriteLine("先买了包盐{0}", state);
Console.WriteLine("顺便买了包烟{0}", state);
}, "task1");
await Task.Factory.StartNew((state) =>
{
Console.WriteLine("有菜没酒,快乐减半{0}", state);
Console.WriteLine("再去买瓶酒{0}", state);
}, "task2");
Console.WriteLine("买完东西,回家了");
}
#endregion
#region 买盐为例 取消异步
public static void CookDinner_CancelBuySalt()
{
Console.WriteLine("老婆开始做饭");
Console.WriteLine("哎呀,没盐了");
CancellationTokenSource source = new CancellationTokenSource();
Task<string> task = CommandBuySalt_CancelBuySalt(source.Token);
Console.WriteLine("不管他继续炒菜");
Thread.Sleep(100);
string result = "家里的盐";
if (!string.IsNullOrEmpty(result))
{
source.Cancel(); //传达取消请求
Console.WriteLine("家里有盐不用买了");
}
else
{
result = task.Result;
}
Console.WriteLine("既然有盐我就继续炒菜{0}", result);
Console.WriteLine("老婆把菜做好了");
Console.WriteLine("任务的状态:{0},已完成:{1},已取消:{2},已失败{3}",
task.Status, task.IsCanceled, task.IsCanceled, task.IsFaulted);
}
public static async Task<string> CommandBuySalt_CancelBuySalt(CancellationToken token)
{
Console.WriteLine("我准备出去买盐");
//已开始执行的任务不能被取消
string result = await Task.Run(() =>
{
Console.WriteLine("屁颠屁颠的去买盐");
Thread.Sleep(1000);
}, token).ContinueWith((t) => //若没有取消就继续执行
{
Console.WriteLine("盐已经买好了");
Thread.Sleep(1000);
return "盐买回来了,顺便我还买了一包烟";
}, token);
Console.WriteLine("{0}", result);
return result;
}
#endregion
#region 买盐为例 多个 CancellationTokenSource 取消异步任务,以及注册取消后的回调委托方法
public static void CookDinner_MultiCancelBuySalt()
{
Console.WriteLine("老婆开始做饭");
Console.WriteLine("哎呀,没盐了");
CancellationTokenSource source1 = new CancellationTokenSource();
CancellationTokenSource source2 = new CancellationTokenSource();
CancellationTokenSource source = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);
//注册取消时的回调委托
source1.Token.Register(() =>
{
Console.WriteLine("因为{0}所以取消", "家里有盐");
});
source2.Token.Register((state) =>
{
Console.WriteLine("因为{0},所以取消", state);
},"不做了出去吃");
source.Token.Register((state) =>
{
Console.WriteLine("因为{0}所以取消", state);
},"没理由");
//这里必须传递 CancellationTokenSource.CreateLinkedTokenSour1e() 方法返回的 Token 对象
Task<string> task = CommandBuySalt_MultiCancelBuySalt(source.Token);
Console.WriteLine("等等,好像不用买了");
Thread.Sleep(100);
string[] result = new string[] { "家里的盐", "不做了出去吃", "没理由" };
Random r = new Random();
switch (r.Next(1,4))
{
case 1:
source1.Cancel(); //传达取消请求(家里有盐)
//source1.CancelAfter(3000); //3s后才调用取消的回调方法
Console.WriteLine("既然有盐我就继续炒菜{0}", result[0]);
break;
case 2:
source2.Cancel(); //传达取消请求(不做了出去吃)
//source2.CancelAfter(3000); //3s后才调用取消的回调方法
Console.WriteLine("我们出去吃不用买啦{0}", result[1]);
break;
case 3:
source.Cancel();
Console.WriteLine("没理由就是不用买啦{0}", result[2]);
break;
default:
break;
}
Console.WriteLine("最终的任务状态是:{0},已完成{1},已取消{2},已失败{3}",
task.Status,task.IsCanceled,task.IsCanceled,task.IsFaulted);
}
/// <summary>
/// 通知我去买盐(又告诉我不用买了,各种理由)
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static async Task<string> CommandBuySalt_MultiCancelBuySalt(CancellationToken token)
{
Console.WriteLine("这时我准备去买盐了");
//已开始执行的任务不能被取消
string result = await Task.Run(() =>
{
Console.WriteLine("屁颠屁颠的去买盐");
Thread.Sleep(1000);
}, token).ContinueWith((t) => //若没有取消就继续执行
{
Console.WriteLine("盐已经买好了");
Thread.Sleep(1000);
return "盐买回来了,顺便我还买了一包烟";
},token);
Console.WriteLine("{0}",result);
return result;
}
#endregion
}