C#多线程与异步
1、什么是异步同步
如果一个方法被调用,调用者需要等待该方法被执行完毕之后才能继续执行,则是同步。
如果方法被调用后立刻返回,即使该方法是一个耗时操作,也能立刻返回到调用者,调用者不需要等待该方法,则称之为异步。
异步编程需要用到Task任务函数,不返回值的任务由 System.Threading.Tasks.Task 类表示。返回值的任务由 System.Threading.Tasks.Task<TResult> 类表示,该类从Task 继承。
C#提供了基于任务的异步编程方法(TPL),更多资料在《.NET 中的并行编程》https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/
先来个例子看下Task的基本用法。
static void Main(string[] args)
{
var task = new Task(() =>
{
Console.WriteLine($"hello, task的线程ID为{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine("task start....");
Thread.Sleep();
Console.WriteLine("task end....");
});
task.Start();
Console.WriteLine("main start....");
Thread.Sleep();
Console.WriteLine("main end....");
Console.ReadLine();
}
运行结果为:

2、Task的三种启动方法
通过new方法创建,然后start启动。
通过Task.Factory.StartNew创建。
通过Task.Run创建。
Task task1 = new Task(() =>
Console.WriteLine($"hello, task 1的线程ID{Thread.CurrentThread.ManagedThreadId}");});
task1.Start(); Task task2 = Task.Run(() =>
{Console.WriteLine($"hello, task 2的线程ID{Thread.CurrentThread.ManagedThreadId}");}); Task task3 = Task.Factory.StartNew(() =>
{Console.WriteLine($"hello,task 3的线程ID为{Thread.CurrentThread.ManagedThreadId}");});
3、Task阻塞与等待
如果要等待任务完成可以使用Task.Wait方法,使用WaitAll方法等待所有任务完成。如果等待多个任务但是只需要任何一个任务完成就可以执行下一步,则可以使用WaitAny。
static void Main(string[] args)
{
var t1 = Task.Run(() =>
{
Thread.Sleep();
Console.WriteLine($"t1线程ID为{ Thread.CurrentThread.ManagedThreadId}");
}); var t2 = Task.Run(() =>
{
Thread.Sleep();
Console.WriteLine($"t2线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
// t1.Wait();
var t3 = Task.Run(() =>
{
Console.WriteLine($"t3线程ID为{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine("t3 start....");
Thread.Sleep();
Console.WriteLine("t3 end....");
});
Console.WriteLine("main start....");
Thread.Sleep();
Console.WriteLine("main end....");
Console.ReadLine();
}
运行效果:

如果打开t1.Wait(); 如下t1先执行。

如果t1.Wait()改为:Task.WaitAll(t1, t2);输出如下,先等待t1和t2执行完毕。

如果需要某个任务先执行完再运行主线程,则使用RunSynchronously就可以同步执行Task任务。下面的例子就是在当前的调度器上同步的执行任务t1。
var t1 = new Task(() =>
{
Thread.Sleep();
Console.WriteLine($"t1线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
t1.RunSynchronously();
Console.WriteLine($"主线程ID为{ Thread.CurrentThread.ManagedThreadId}");
Console.ReadLine();

4、任务的状态和返回值
Task<Tresult>获取异步任务的返回值。
var t1 = Task.Run(() =>
{
Thread.Sleep();
Console.WriteLine($"t1线程ID为{ Thread.CurrentThread.ManagedThreadId}");
return "t1 return";
});
Console.WriteLine($"t1 return value {t1.Result}");
Task<Tresult>会阻塞任务直到任务返回,Task.Result给出任务的返回值给调用它的任务,下面例子显示了Result等待任务完成。
var cts = new CancellationTokenSource();
Task<int> t1 = new Task<int>(() =>
{
try
{
//while (!cts.IsCancellationRequested)
{
//cts.Token.ThrowIfCancellationRequested();
Console.WriteLine($"t1 start 线程ID为{ Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep();
Console.WriteLine($"t1 end 线程ID为{ Thread.CurrentThread.ManagedThreadId}");
}
return ;
}
catch (Exception ex)
{
Console.WriteLine("---Exception---");
return ;
}
}, cts.Token);
t1.Start();
Console.WriteLine("---end---{0}", t1.Result);
输出结果如下,t.Result为1.

t.Result会导致阻塞直至等待的任务完成返回。如果使用返回值,那么异步方法中方法签名返回值为Task<T>,代码中的返回值也要为T。
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
Task<int> t1 = new Task<int>(() =>
{
try
{
//while (!cts.IsCancellationRequested)
{
//cts.Token.ThrowIfCancellationRequested();
Console.WriteLine($"t1 start 线程ID为{ Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep();
Console.WriteLine($"t1 end 线程ID为{ Thread.CurrentThread.ManagedThreadId}");
}
return ;
}
catch (Exception ex)
{
Console.WriteLine("---Exception---");
return ;
}
}, cts.Token); t1.Start();
// Console.WriteLine("---end---{0}", t1.Result);
var t2 = Task.Run(() =>
{
Thread.Sleep();
Console.WriteLine($"t2线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
Console.ReadLine();
}
}
输出为:

如果打开注释Console.WriteLine("---end---{0}", t1.Result);则任务阻塞,等待t1执行完,拿到返回值t1.Result之后才继续执行,输出为:

5、任务的取消
任务出错或用户取消了操作,则需要用到任务的取消TaskCanceledException。
cts.Cancel()设置了cts.IsCancellationRequested为true,
然后cts.Token.ThrowIfCancellationRequested()这一句抛出了异常。

执行结果为:

如果主线程不延时或延时为10,则有可能在任务启动之前就停止了任务,就不会发生任务抛出异常了,由于系统调度问题,一定情况下输出与上面的一样,大部分情况输出为:

此外,也可以在task中查询标志。while (!cts.IsCancellationRequested),如果取消则不在执行。如果一次取消多个任务,可以使用CancellationTokenSource.CreateLinkedTokenSource实现。
帮助文档:
从开始.NET Framework 4,.NET Framework 使用统一的模型来协作取消异步操作或长时间运行的同步操作涉及两个对象:
一个CancellationTokenSource对象,它提供取消标记通过其Token属性,并将取消消息通过调用其Cancel或CancelAfter方法。
一个CancellationToken对象,指示是否请求取消。
用于实现协作取消模型的常规模式是:
实例化 CancellationTokenSource 对象,此对象管理取消通知并将其发送给单个取消标记。
将 CancellationTokenSource.Token 属性返回的标记传递给每个侦听取消的任务或线程。
调用CancellationToken.IsCancellationRequested从接收取消标记的操作的方法。 提供有关每个任务或线程来响应取消请求的机制。 无论你选择取消操作和完全操作方式,取决于你的应用程序逻辑。
调用 CancellationTokenSource.Cancel 方法以提供取消通知。 这将设置CancellationToken.IsCancellationRequested到取消标记的每个副本上的属性true。
调用Dispose方法在您使用完CancellationTokenSource对象。
6、Task和Thread
Task是基于Thread的,是比较高层级的封装,Task任务最终还是需要Thread来执行。
Task比Thread的开销小,Task默认使用线程池。
Task默认使用后台线程来执行,Thread默认是前台线程。
Task使用参数和返回值都比Thread简单。
Task的多任务调度比Thread灵活,Thead的join需要挂起调用者去执行被调用线程然后返回到主线程。而Task提供了多种Wait函数,可以等待其他线程执行。
7、参考文档
本文只是学习笔记,水平有限,抛砖迎玉。
1、基于任务的异步编程
2、Task 类
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task?view=netframework-4.8
3、Difference Between Task and Thread
https://www.dotnetforall.com/difference-task-and-thread/
4、Task And Thread In C#
https://www.c-sharpcorner.com/article/task-and-thread-in-c-sharp/
5、C#多线程和异步(一)——基本概念和使用方法
C#多线程与异步的更多相关文章
- 从Nginx的Web请求处理机制中剖析多进程、多线程、异步IO
Nginx服务器web请求处理机制 从设计架构来说,Nginx服务器是与众不同的.不同之处一方面体现在它的模块化设计,另一方面,也是最重要的一方面,体现在它对客户端请求的处理机制上. Web服务器和客 ...
- 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
[源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...
- 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithProgress, IAsyncOperationWithProgress
[源码下载] 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithPro ...
- c# 中的多线程和异步
前言: 1.异步和多线程有区别吗? 答案:多线程可以说是实现异步的一种方法方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击.拖拽.输入字符等.使主程序看起来实时都保持着等待用户响应的状态 ...
- 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理
1. 概述 本章主要讲解.net4.5如何实现多线程和异步处理的相关内容. 2. 主要内容 2.1 理解线程 ① 使用Thread类 public static class Program { ...
- Android Learning:多线程与异步消息处理机制
在最近学习Android项目源码的过程中,遇到了很多多线程以及异步消息处理的机制.由于之前对这块的知识只是浅尝辄止,并没有系统的理解.但是工程中反复出现让我意识到这个知识的重要性.所以我整理出这篇博客 ...
- Android多线程及异步处理问题
1.问题提出 1)为何需要多线程? 2)多线程如何实现? 3)多线程机制的核心是啥? 4)到底有多少种实现方式? 2.问题分析 1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到 ...
- 初步谈谈 C# 多线程、异步编程与并发服务器
多线程与异步编程可以达到避免调用线程异步阻塞作用,但是两者还是有点不同. 多线程与异步编程的异同: 1.线程是cpu 调度资源和分配的基本单位,本质上是进程中的一段并发执行的代码. 2.线程编程的思维 ...
- Linux 多线程 - 线程异步与同步机制
Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...
- [Xcode 实际操作]八、网络与多线程-(22)使用GCD多线程技术异步下载图片
目录:[Swift]Xcode实际操作 本文将演示如何使用使用GCD多线程技术异步下载图片. Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法 ...
随机推荐
- DOMjudge配置
DOMjudge配置补充 系统环境为 Debbian GNU/Linux 9 (stretch) 64-bit 在Web server configuration中, ln -s etc/apache ...
- 洛谷P3377 【模板】左偏树(可并堆) 题解
作者:zifeiy 标签:左偏树 这篇随笔需要你在之前掌握 堆 和 二叉树 的相关知识点. 堆支持在 \(O(\log n)\) 的时间内进行插入元素.查询最值和删除最值的操作.在这里,如果最值是最小 ...
- java 多线程安全问题的解决方法
三种方法: 同步代码块: synchronized(obj) { //obj表示同步监视器,是同一个同步对象 /**..... TODO SOMETHING */ } 同步方法 格式: 在方法上加 ...
- 在CentOS7上安装ftp服务器用于保存服务端上传的图片。
1.CentOS卸载vsftpd的方法 如果服务器上已经安装了vsftpd服务,配置出错需要卸载vsftpd服务. 1.1 查找vsftpd服务 [root@localhost /]# rpm -aq ...
- 2018-8-10-UWP-分享用那个图标
title author date CreateTime categories UWP 分享用那个图标 lindexi 2018-08-10 19:16:52 +0800 2018-2-13 17:2 ...
- HDU 1828“Picture”(线段树+扫描线求矩形周长并)
传送门 •参考资料 [1]:算法总结:[线段树+扫描线]&矩形覆盖求面积/周长问题(HDU 1542/HDU 1828) •题意 给你 n 个矩形,求矩形并的周长: •题解1(两次扫描线) 周 ...
- H3C 主机名与IP地址映射需求
- ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(3)
接下来完成用户.角色的增删查改,以及用户角色.权限的设置 对用户表.角色表做了一些扩展如下[可以更加自己需要增减字段] 相应的M_UserProfile.cs.M_Roles.cs进行扩展 using ...
- 2018-8-10-C#-写系统日志
title author date CreateTime categories C# 写系统日志 lindexi 2018-08-10 19:16:53 +0800 2018-2-13 17:23:3 ...
- 网摘-获取屏幕dc并且将其画面显示在窗体中
获取屏幕dc并且将其画面显示在窗体中 HWND hWnd = ::GetDesktopWindow();//获得屏幕的HWND. HDC hScreenDC = ::GetDC(hWnd); // ...