.NET多线程(Thread,ThreadPool,Task,Async与Await)
.NET多线程是什么?
进程与线程
进程是一种正在执行的程序。
线程是程序中的一个执行流。
多线程是指一个程序中可以同时运行多个不同的线程来执行不同的任务。
.NET中的线程
Thread是创建和控制线程的类。
ManagedThreadId是线程ID。
CurrentThread是获取当前正在运行的线程。
同步与异步
同步是调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。(单线程)
异步调用一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。(多线程)
.NET中的多线程发展
主要有Thread,ThreadPool,Task
Thread就是线程,需要自己调度,直接跟系统对接,相对管理比较复杂及效率差。
ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的。但是ThreadPool提供的接口比较少。
Task和ThreadPool是一样的,都是从线程池中取空闲的线程。比ThreadPool调用接口更加丰富。目前.Net使用多线程管理,应该优先使用Task。
代码:
/// <summary>
/// 多线程发展历史
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnHistory_Click(object sender, EventArgs e)
{
Console.WriteLine($"Thread start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
var threadStart = new ThreadStart(DoNothing);
var thread = new Thread(threadStart);
thread.Start();
Console.WriteLine($"Thread end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(3000);
Console.WriteLine($"ThreadPool start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
var callback = new WaitCallback(DoNothing);
ThreadPool.QueueUserWorkItem(callback);
Console.WriteLine($"ThreadPool end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(3000);
Console.WriteLine($"Task start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Action action = DoNothing;
Task task = new Task(action);
task.Start();
Console.WriteLine($"Task end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
为什么需要多线程?
特点:
- 卡界面:单线程卡,多线程不卡
- 性能好:单线程差,多线程好(资源换性能)
- 执行顺序:单线程顺序,多线程无序
代码:
/// <summary>
/// 同步(单线程)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnSync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
for (int i = 0; i < 5; i++)
{
DoNothing();
}
Console.WriteLine($"btnSync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
/// <summary>
/// 异步(多线程)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsync_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnAsync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
for (int i = 0; i < 5; i++)
{
var ts = new ThreadStart(DoNothing);
var t = new Thread(ts);
t.Start();
}
Console.WriteLine($"btnAsync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
private void DoNothing()
{
Console.WriteLine($"DoNothing start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
Console.WriteLine($"DoNothing end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
如何使用.NET多线程?
Task
创建任务
1、通过调用任务类构造函数实例化,但通过调用其Start()启动任务。
Task t1 = new Task(action, "alpha");
t1.Start();
2、通过调用TaskFactory.StartNew(Action ,Object)方法在单个方法调用中实例化和启动任务。
Task t2 = Task.Factory.StartNew(action, "beta");
3、通过调用Run(Action)方法在单个方法调用中实例化和启动任务。
Task t3 = Task.Run(action);
从任务中返回值
Result 属性将阻止调用线程,直到任务完成。
Task<int> task1 = Task<int>.Factory.StartNew(() => 1);
int i = task1.Result;
等待任务完成
可以通过调用 Wait 方法来等待一个或多个任务完成,从而同步调用线程的执行以及它启动的异步任务。
调用无参数 Wait() 方法以无条件等待,直到任务完成。
调用Wait(Int32)和 Wait(TimeSpan) 方法会阻止调用线程,直到任务完成或超时间隔(以先达到者为准)为止。
调用WaitAny(Task[])方法等待一组任务中第一个任务完成。
调用WaitAll(Task[])方法来等待一系列任务全部完成。
异常处理
调用代码可以通过使用 try/catch 块中的以下任意方法来处理异常:
- await task
- task.Wait()
- task.Result
- task.GetAwaiter().GetResult()
代码:
var task1 = Task.Run(() => { throw new Exception("This exception is expected!"); });
try
{
task1.Wait();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
取消任务
你可以使用 CancellationTokenSource 类在以后某一时间发出取消请求。
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var tasks = new ConcurrentBag<Task>();
Task tc;
for (int i = 0; i < 10; i++)
{
var k = i;
tc = Task.Run(() => DoNothing(k, token), token);
tasks.Add(tc);
}
char ch = Console.ReadKey().KeyChar;
if (ch == 'c' || ch == 'C')
{
tokenSource.Cancel();
Console.WriteLine("\n开始取消任务.");
}
try
{
Task.WhenAll(tasks.ToArray());
}
catch (OperationCanceledException)
{
Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n");
}
finally
{
tokenSource.Dispose();
}
Console.ReadKey();
}
private static void DoNothing(int i, CancellationToken ct)
{
Console.WriteLine($"DoNothing start index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(i * 1000);
if (ct.IsCancellationRequested)
{
Console.WriteLine($"任务已取消 index:{i} id:{Thread.CurrentThread.ManagedThreadId} ");
ct.ThrowIfCancellationRequested();
}
Console.WriteLine($"DoNothing end index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
实际案例
代码:
/// <summary>
/// Task实际案例
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTask_Click(object sender, EventArgs e)
{
Console.WriteLine($"项目组接到任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Console.WriteLine($"项目经理设计数据库,设计原型,分配任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() => Coding("赵XX","前端页面")));
tasks.Add(Task.Run(() => Coding("王XX", "IOS页面")));
tasks.Add(Task.Run(() => Coding("黄XX", "后端接口")));
tasks.Add(Task.Run(() => Coding("杜XX", "后端接口")));
TaskFactory taskFactory = new TaskFactory();
taskFactory.ContinueWhenAll(tasks.ToArray(), t =>
{
Console.WriteLine($"项目经理发布,测试人员测试任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
});
}
private void Coding(string personName,string taskName)
{
Console.WriteLine($"{personName}开发{taskName} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
}
async和await
Async 和 Await几乎与创建同步方法一样创建异步方法。
代码:
/// <summary>
/// Async和Await应用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsyncAndAwait_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnAsyncAndAwait_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
DoNothingAsync();
Console.WriteLine($"btnAsyncAndAwait_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
private async Task DoNothingAsync()
{
Console.WriteLine($"DoNothingAsync start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
await Task.Run(() => {
Console.WriteLine($"DoNothingAsync Task start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
Console.WriteLine($"DoNothingAsync Task end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
});
Console.WriteLine($"DoNothingAsync end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}
参考
- 基于任务的异步模式 https://docs.microsoft.com/zh-CN/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap
- 使用 Async 和 Await 的异步编程 https://docs.microsoft.com/zh-CN/dotnet/csharp/programming-guide/concepts/async/
.NET多线程(Thread,ThreadPool,Task,Async与Await)的更多相关文章
- c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习
c#中@标志的作用 参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...
- 异步多线程 Thread ThreadPool Task
一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...
- .net 多线程 Thread ThreadPool Task
先准备一个耗时方法 /// <summary>/// 耗时方法/// </summary>/// <param name="name">< ...
- 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读
记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...
- Thread,ThreadPool,Task, 到async await 的基本使用方法和理解
很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...
- 多线程编程学习笔记——async和await(二)
接上文 多线程编程学习笔记——async和await(一) 三. 对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...
- 多线程编程学习笔记——async和await(三)
接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五. 处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...
- Thread,ThreadPool,Task
线程分为前台和后台.比如我们直接new一个Thread这就是前台线程. 前台线程一定会执行. 比如我们创建2个线程:1号,2号,同时执行,假设1号是主线程,1执行完了,依旧会等待2执行完成,整个程序才 ...
- 多线程编程学习笔记——async和await(一)
接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...
随机推荐
- 同步博客到cnblogs平台
缘由 最最开始在csdn写博客,广告太多,平台暗调资源积分,退:后来使用githubpage+jeklly搭建静态博客,感觉不错,回归到安静的敲打环境.emmmm,由于是静态博客项目,虽能最大化自定义 ...
- 快速入门Mybatis
框架概述 什么是框架 它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题.使用框架的好处:框架封装了很多的细节,使开发者可以使用极简的方式实现功能.大大提高开发效率 三层架构 UI(表现层 ...
- Spark优化之小文件是否需要合并?
我们知道,大部分Spark计算都是在内存中完成的,所以Spark的瓶颈一般来自于集群(standalone, yarn, mesos, k8s)的资源紧张,CPU,网络带宽,内存.Spark的性能,想 ...
- python设计模式之命令模式
python设计模式之命令模式 现在多数应用都有撤销操作.虽然难以想象,但在很多年里,任何软件中确实都不存在撤销操作.撤销操作是在1974年引入的,但Fortran和Lisp分别早在1957年和195 ...
- 关于dubbo扩展点的一点分析
扩展点能力 能load class,这个class除了顶层接口class(在ExtensionLoader中对应type字段),还能load各实现类的class. 能创建instance. 能指定这个 ...
- go语言gRPC系列(三) - 使用grpc-gateway同时提供HTTP和gRPC服务
1. gRPC提供HTTP服务 1.1 存在的意义 1.2 代码示例 1.3 使用postman尝试调用 1.4 gRPC客户端代码调用 2. 使用grpc-gateway同时提供HTTP和gRPC服 ...
- 用过 mongodb 吧, 这三个大坑踩过吗?
一:背景 1. 讲故事 前段时间有位朋友在微信群问,在向 mongodb 中插入的时间为啥取出来的时候少了 8 个小时,8 在时间处理上是一个非常敏感的数字,又吉利又是一个普适的话题,后来我想想初次使 ...
- SweetAlert 弹框之后点击OK执行方法
swal( '审核通过!', '', 'success' ).then(function () { Return(); })
- Android SDK 环境的搭建 --图形界面模式和命令行模式
Android 开发首先就是要搭建开发环境,没有用过Eclipse(ADT)开发过,直接用的Android Studio,其中最主要的就是 Android SDK的安装和搭建,所以这里只是总结下And ...
- Distributional Reinforcement Learning with Quantile Regression
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! arXiv:1710.10044v1 [cs.AI] 27 Oct 2017 In AAAI Conference on Artifici ...