买公司的网站,大连做企业网站排名,上海公司注册代理服务,长沙本地论坛有哪些文章目录 1.什么是异步2.Task 产生背景3.Thread(线程) 和 Task(异步)的区别3.1 几个名词3.2 Thread 与 Task 的区别 4.Task API4.1 创建和启动任务4.2 Task 等待、延续和组合4.3 task.Result4.4 Task.Delay() 和 Thread.Sleep() 区别 5.CancellationToken 和 CancellationToken… 文章目录 1.什么是异步2.Task 产生背景3.Thread(线程) 和 Task(异步)的区别3.1 几个名词3.2 Thread 与 Task 的区别 4.Task API4.1 创建和启动任务4.2 Task 等待、延续和组合4.3 task.Result4.4 Task.Delay() 和 Thread.Sleep() 区别 5.CancellationToken 和 CancellationTokenSource 取消线程5.1 CancellationToken5.2 CancellationTokenSource5.3 示例 6.async 与 await7.微软案例 1.什么是异步 同步和异步主要用于修饰方法。当一个方法被调用时调用者需要等待该方法执行完毕并返回才能继续执行我们称这个方法是同步方法当一个方法被调用时立即返回并获取一个线程执行该方法内部的业务调用者不用等待该方法执行完毕我们称这个方法为异步方法。 异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成)因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类微软极力推荐使用Task来执行异步任务现在C#类库中的异步方法基本都用到了Tasknet5.0推出了async/await让异步编程更为方便。
2.Task 产生背景 Task出现之前微软的多线程处理方式有Thread→ThreadPool→委托的异步调用虽然也可以基本业务需要的多线程场景但它们在多个线程的等待处理方面、资源占用方面、线程延续和阻塞方面、线程的取消方面等都显得比较笨拙在面对复杂的业务场景下显得有点捉襟见肘了。 ThreadPool相比Thread来说具备了很多优势但是ThreadPool却又存在一些使用上的不方便。比如
ThreadPool不支持线程的取消、完成、失败通知等交互性操作ThreadPool不支持线程执行的先后次序
正是在这种背景下Task应运而生。Task是微软在.Net 4.0时代推出来的也是微软极力推荐的一种多线程的处理方式Task看起来像一个Thread实际上它是在ThreadPool的基础上进行的封装Task的控制和扩展性很强在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool。以下是一个简单的任务示例
static void Main(string[] args)
{Task t new Task(() {Console.WriteLine(任务开始工作……);Thread.Sleep(5000); //模拟工作过程});t.Start();t.ContinueWith(task {Console.WriteLine(任务完成完成时候的状态为);Console.WriteLine(IsCanceled{0}\tIsCompleted{1}\tIsFaulted{2}, task.IsCanceled, task.IsCompleted, task.IsFaulted);});Console.ReadKey();
}3.Thread(线程) 和 Task(异步)的区别
3.1 几个名词
1、进程process 当一个程序开始运行时它就是一个进程进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。2、线程thread 线程是程序中的一个执行流每个线程都有自己的专有寄存器(栈指针、程序计数器等)但代码区是共享的。多线程是指程序中包含多个执行流即在一个程序中可以同时运行多个不同的线程来执行不同的任务也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。 前台线程 前台线程是不会被立即关闭的它的关闭只会发生在自己执行完成时不受外在因素的影响。假如应用程序退出造成它的前台线程终止此时CLR仍然保持活动并运行使应用程序能继续运行当它的的前台线程都终止后整个进程才会被销毁。(Thread类默认创建的是前台线程)后台线程 后台线程是可以随时被CLR关闭而不引发异常的也就是说当后台线程被关闭时资源的回收是立即的不等待的也不考虑后台线程是否执行完成就算是正在执行中也立即被终止。(通过线程池/Task创建的线程都是后台线程) 3、同步sync: 发出一个功能调用时在没有得到结果之前该调用就不返回。4、异步async: 与同步相对调用在发出之后这个调用就直接返回了所以没有返回结果。当这个调用完成后一般通过状态、通知和回调来通知调用者。对于异步调用调用的返回并不受调用者控制。 通知调用者的三种方式 状态即监听被调用者的状态轮询调用者需要每隔一定时间检查一次效率会很低。通知当被调用者执行完成后发出通知告知调用者无需消耗太多性能。回调与通知类似当被调用者执行完成后会调用调用者提供的回调函数。 5、阻塞block 阻塞调用是指调用结果返回或者收到通知之前当前线程会被挂起即不继续执行后续操作。简单来说等前一件做完了才能做下一件事。6、非阻塞non-block 非阻塞调用指在不能立刻得到结果之前该调用不会阻塞当前线程。
3.2 Thread 与 Task 的区别
Thread 类主要用于实现线程的创建以及执行。 Task 类表示以异步方式执行的单个操作。
1、Task 是基于 Thread 的是比较高层级的封装Task 最终还是需要 Thread 来执行 2、Task 默认使用后台线程执行Thread 默认使用前台线程
static void Main(string[] args)
{Thread thread new Thread(obj { Thread.Sleep(3000); });thread.Start();
}// 上面代码tread为前台线程主程序在3秒后结束。static void Main(string[] args)
{Taskint task new Taskint(() {Thread,Sleep(3000);return 1;});task.Start();
}// 上面代码task为后台线程主程序会瞬间结束。3、Task 可以有返回值Thread 没有返回值
public static void Main(string[] args)
{Taskint task new Taskint(LongRunningTask);task.Start();Console.WriteLine(task.Result);
} private static int LongRunningTask()
{Thread.Sleep(3000);return 1;
}4、Task 可以执行后续操作Thread 不能执行后续操作
4.Task API
4.1 创建和启动任务
不带返回值
//1. new方式实例化一个Task需要通过Start方法启动
Task task1 new Task(()
{Thread.Sleep(100);Console.WriteLine($hello, task1的线程ID为{Thread.CurrentThread.ManagedThreadId});
});
task1.Start();//2. Task.Factory.StartNew(Action action)创建和启动一个Task
Task task2 Task.Factory.StartNew(()
{Thread.Sleep(100);Console.WriteLine($hello, task2的线程ID为{ Thread.CurrentThread.ManagedThreadId});
});//3. Task.Run(Action action)将任务放在线程池队列返回并启动一个Task
Task task3 Task.Run(()
{Thread.Sleep(100);Console.WriteLine($hello, task3的线程ID为{ Thread.CurrentThread.ManagedThreadId});
});Console.WriteLine(执行主线程);
Console.ReadKey();执行主线程 hello, task1的线程ID为4 hello, task2的线程ID为6 hello, task3的线程ID为7 带返回值
// 1.new方式实例化一个Task需要通过Start方法启动
Taskstring task1 new Taskstring(()
{return $hello, task1的ID为{Thread.CurrentThread.ManagedThreadId};
});
task1.Start();// 2.Task.Factory.StartNew(Func func)创建和启动一个Task
Taskstring task2 Task.Factory.StartNewstring(()
{return $hello, task2的ID为{ Thread.CurrentThread.ManagedThreadId};
});// 3.Task.Run(Func func)将任务放在线程池队列返回并启动一个Task
Taskstring task3 Task.Runstring(()
{return $hello, task3的ID为{ Thread.CurrentThread.ManagedThreadId};
});Console.WriteLine(执行主线程);
Console.WriteLine(task1.Result);// 注意task.Result获取结果时会阻塞UI主线程
Console.WriteLine(task2.Result);
Console.WriteLine(task3.Result);
Console.ReadKey();执行主线程 hello, task1的ID为4 hello, task2的ID为6 hello, task3的ID为7 4.2 Task 等待、延续和组合
Wait 针对单个Task的实例可以task1.wait进行线程等待阻塞主线程WaitAny 线程列表中任何一个线程执行完毕即可执行阻塞主线程WaitAll 线程列表中所有线程执行完毕方可执行阻塞主线程WhenAny 与ContinueWith配合,线程列表中任何一个执行完毕则继续ContinueWith中的任务开启新线程不阻塞主线程WhenAll 与ContinueWith配合,线程列表中所有线程执行完毕则继续ContinueWith中的任务开启新线程不阻塞主线程ContinueWith 与WhenAny或WhenAll配合使用ContinueWhenAny 等价于Task的WhenAnyContinueWithContinueWhenAll 等价于Task的WhenAllContinueWith
//创建一个任务
Taskint task Task.Runint(()
{int sum 0;Console.WriteLine(使用Task执行异步操作.);for (int i 0; i 1000; i){sum i;}return sum;
});Console.WriteLine(主线程执行其他处理);
//任务完成时执行处理。
Task cwt task.ContinueWith(t
{Console.WriteLine(任务完成后的执行结果{0}, t.Result.ToString());
});task.Wait();
cwt.Wait();Actionstring,int log (name,time)
{Console.WriteLine(${name}任务开始...);Thread.Sleep(time);Console.WriteLine(${name}任务结束!);
};
ListTask tasks new ListTask
{Task.Run(() log(张三,3000)),Task.Run(() log(李四,1000)),Task.Run(() log(王五,2000))
};
//以下语句逐个测试效果
Task.WaitAny(tasks.ToArray());
Task.WaitAll(tasks.ToArray());
Task.WhenAny(tasks.ToArray()).ContinueWith(x Console.WriteLine(某个Task执行完毕));
Task.WhenAll(tasks.ToArray()).ContinueWith(x Console.WriteLine(所有Task执行完毕));
Task.Factory.ContinueWhenAny(tasks.ToArray(), x Console.WriteLine(某个Task执行完毕));
Task.Factory.ContinueWhenAll(tasks.ToArray(), x Console.WriteLine(所有Task执行完毕));Console.ReadKey();4.3 task.Result
等待获取task返回值阻塞调用其他线程直到当前异步操作完成相当于调用wait方法
static void Main(string[] args)
{Taskstring task Task.Runstring(() {Thread.Sleep(3000);return ming_堵塞线程;});Console.WriteLine(task.Result);Console.WriteLine(主线程执行);Console.ReadKey();
}ming_堵塞线程 主线程执行 4.4 Task.Delay() 和 Thread.Sleep() 区别
Thread.Sleep()是同步延迟 Task.Delay()是异步延迟。Thread.Sleep()会阻塞线程 Task.Delay()不会。Thread.Sleep()不能取消 Task.Delay()可以。Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行在同步代码中使用Task.Delay()是没有意义的在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。
// 阻塞出现CPU等待...
static void Main(string[] args)
{// 阻塞出现CPU等待...Task.Factory.StartNew(() {Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) ****** Start Sleep()******);for (int i 1; i 10; i){Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) ******Sleep****** i);Thread.Sleep(1000);//同步延迟阻塞一秒}Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) ******End Sleep()******);Console.WriteLine();});// 不阻塞Task.Factory.StartNew(() {Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) StartDelay());for (int i 1; i 10; i){Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) Delay i);Task.Delay(1000);//异步延迟}Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) End Delay());Console.WriteLine();});// 不阻塞等待三秒Task.Factory.StartNew(async() {Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) StartDelay());for (int i 1; i 10; i){Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) Await Delay i);await Task.Delay(1000);//异步延迟}Console.WriteLine(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff) End Delay());Console.WriteLine();});Console.ReadKey();
}5.CancellationToken 和 CancellationTokenSource 取消线程
5.1 CancellationToken
属性
//表示当前CancellationToken是否可以被取消
public bool CanBeCanceled { get; }
//表示当前CancellationToken是否已经是取消状态
public bool IsCancellationRequested { get; }方法
//往CancellationToken中注册回调
public CancellationTokenRegistration Register(Action callback);
//当CancellationToken处于取消状态时抛出System.OperationCanceledException异常
public void ThrowIfCancellationRequested();5.2 CancellationTokenSource
属性
//表示Token是否已处于取消状态
public bool IsCancellationRequested { get; }
//CancellationToken 对象
public CancellationToken Token { get; }方法
//立刻取消
public void Cancel();
//立刻取消
public void Cancel(bool throwOnFirstException);
//延迟指定时间后取消
public void CancelAfter(int millisecondsDelay);
//延迟指定时间后取消
public void CancelAfter(TimeSpan delay);5.3 示例
CancellationTokenSource source new CancellationTokenSource();
//注册一个线程取消后执行的逻辑
source.Token.Register(()
{//这里执行线程被取消后的业务逻辑.Console.WriteLine(-------------我是线程被取消后的业务逻辑---------------------);
});Task.Run(()
{while (!source.IsCancellationRequested){Thread.Sleep(100);Console.WriteLine(当前thread{0} 正在运行, Thread.CurrentThread.ManagedThreadId);}
}, source.Token);Thread.Sleep(2000);
source.Cancel();当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 当前thread4 正在运行 -------------我是线程被取消后的业务逻辑--------------------- 当前thread4 正在运行 6.async 与 await
async:
async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。异步方法名字后习惯加个Async后缀async 关键字修饰的方法一般包含一个或多个await 表达式或语句如果不包含 await 表达式或语句则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法。async方法可以是下面三种返回类型 TaskTask TResult void 这种返回类型一般用在event事件处理器中,或者用在你只需要任务执行不关心任务执行结果的情况当中。任何其他具有GetAwaiter方法的类型从C#7.0开始
await:
await关键字只能在async 关键字修饰的方法异步方法中使用。await 运算符的操作数通常是以下其中一个 .NET 类型Task、Task、ValueTask 或 ValueTask。 但是任何可等待表达式都可以是 await 运算符的操作数。
示例 无返回值
static void Main(string[] args)
{Console.WriteLine(主线程--开始);var task TestTaskAsync();task.ContinueWith(t Console.WriteLine(TestTaskAsync方法结束后执行));Console.WriteLine(主线程--结束);Console.ReadKey();
}private static async Task TestTaskAsync()
{Console.WriteLine(开始执行TestTaskAsync方法);Task task new Task(() {Console.WriteLine(开始子线程耗时操作);Thread.Sleep(4000);Console.WriteLine(结束子线程耗时操作);});task.Start();await task;Console.WriteLine(await关键字后面的内容 1);
}带返回值
// 方法一使用ContinueWith
Taskint task TestTaskIntAsync();
task.ContinueWith((t)
{COnsole.WriteLine($TestTaskIntAsync的返回值是{t.Result.ToString()});
});
// 方法二使用await
Taskint task TestTaskIntAsync();
int result await task;
Console.WriteLine($TestTaskIntAsync的返回值是{result });7.微软案例
以微软文档的做早餐的案例加以简化来讲解 1.同步执行
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;namespace ThreadTest
{class Program{static void Main(string[] args){Stopwatch stopwatch new Stopwatch();stopwatch.Start();PourOJ();PourCoffee();ToastBread();FryBacon();FryEggs();Console.WriteLine(早餐已经做完!);stopwatch.Stop();Console.WriteLine($做早餐总计耗时{stopwatch.ElapsedMilliseconds});Console.ReadLine();}//倒橙汁private static void PourOJ(){Thread.Sleep(1000);Console.WriteLine(倒一杯橙汁);}//烤面包private static void ToastBread(){Console.WriteLine(开始烤面包);Thread.Sleep(3000);Console.WriteLine(烤面包好了);}//煎培根private static void FryBacon(){Console.WriteLine(开始煎培根);Thread.Sleep(6000);Console.WriteLine(培根煎好了);}//煎鸡蛋private static void FryEggs(){Console.WriteLine(开始煎鸡蛋);Thread.Sleep(6000);Console.WriteLine(鸡蛋好了);}//倒咖啡private static void PourCoffee(){Thread.Sleep(1000);Console.WriteLine(倒咖啡);}}
}2.并行执行 如果此时我们每一项任务都有一个单独的人去完成 那么可以如下
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;namespace ThreadTest
{class Program{static void Main(string[] args){Test();Console.ReadLine();}private static void Test(){Stopwatch stopwatch new Stopwatch();stopwatch.Start();ListTask tasks new ListTask() { PourOJ(), ToastBread(), FryBacon(), FryEggs(), PourCoffee() };Task.WhenAll(tasks).ContinueWith((t) {Console.WriteLine(早餐已经做完!);stopwatch.Stop();Console.WriteLine($做早餐总计耗时{stopwatch.ElapsedMilliseconds});});}//倒橙汁private static async Task PourOJ(){await Task.Delay(1000);Console.WriteLine(倒一杯橙汁);}//烤面包private static async Task ToastBread(){Console.WriteLine(开始烤面包);await Task.Delay(3000);Console.WriteLine(烤面包好了);}//煎培根private static async Task FryBacon(){Console.WriteLine(开始煎培根);await Task.Delay(6000);Console.WriteLine(培根煎好了);}//煎鸡蛋private static async Task FryEggs(){Console.WriteLine(开始煎鸡蛋);await Task.Delay(6000);Console.WriteLine(鸡蛋好了);}//倒咖啡private static async Task PourCoffee(){await Task.Delay(1000);Console.WriteLine(倒咖啡);}}
}3.并行且可指定顺序执行 现在呢有个问题不可能每次做早餐你都有那么多帮手同时帮你如果现在要求先倒橙汁然后倒咖啡其余的操作并行执行应该如何操作呢 只需将以上案例的Test 方法修改如下
private static async void Test()
{Stopwatch stopwatch new Stopwatch();stopwatch.Start();await PourOJ();await PourCoffee(); ListTask tasks new ListTask() { ToastBread(), FryBacon(), FryEggs() };await Task.WhenAll(tasks);Console.WriteLine(早餐已经做完!);stopwatch.Stop();Console.WriteLine($做早餐总计耗时{stopwatch.ElapsedMilliseconds});
}