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实现。

帮助文档:

https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtokensource?view=netframework-4.8

从开始.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、基于任务的异步编程

https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/task-based-asynchronous-programming

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#多线程和异步(一)——基本概念和使用方法

https://www.cnblogs.com/wyy1234/p/9166444.html

 

C#多线程与异步的更多相关文章

  1. 从Nginx的Web请求处理机制中剖析多进程、多线程、异步IO

    Nginx服务器web请求处理机制 从设计架构来说,Nginx服务器是与众不同的.不同之处一方面体现在它的模块化设计,另一方面,也是最重要的一方面,体现在它对客户端请求的处理机制上. Web服务器和客 ...

  2. 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换

    [源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...

  3. 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithProgress, IAsyncOperationWithProgress

    [源码下载] 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithPro ...

  4. c# 中的多线程和异步

    前言: 1.异步和多线程有区别吗? 答案:多线程可以说是实现异步的一种方法方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击.拖拽.输入字符等.使主程序看起来实时都保持着等待用户响应的状态 ...

  5. 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理

    1. 概述 本章主要讲解.net4.5如何实现多线程和异步处理的相关内容. 2. 主要内容 2.1 理解线程 ① 使用Thread类   public static class Program   { ...

  6. Android Learning:多线程与异步消息处理机制

    在最近学习Android项目源码的过程中,遇到了很多多线程以及异步消息处理的机制.由于之前对这块的知识只是浅尝辄止,并没有系统的理解.但是工程中反复出现让我意识到这个知识的重要性.所以我整理出这篇博客 ...

  7. Android多线程及异步处理问题

    1.问题提出 1)为何需要多线程? 2)多线程如何实现? 3)多线程机制的核心是啥? 4)到底有多少种实现方式? 2.问题分析 1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到 ...

  8. 初步谈谈 C# 多线程、异步编程与并发服务器

    多线程与异步编程可以达到避免调用线程异步阻塞作用,但是两者还是有点不同. 多线程与异步编程的异同: 1.线程是cpu 调度资源和分配的基本单位,本质上是进程中的一段并发执行的代码. 2.线程编程的思维 ...

  9. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  10. [Xcode 实际操作]八、网络与多线程-(22)使用GCD多线程技术异步下载图片

    目录:[Swift]Xcode实际操作 本文将演示如何使用使用GCD多线程技术异步下载图片. Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法 ...

随机推荐

  1. www的iptables实例

    #!/bin/bash export PATH=/sbin:/usr/sbin:/bin:/usr/bin #加载相关模块 modprobe iptable_nat modprobe ip_nat_f ...

  2. Python--day22--面向对象的交互

    Python里面自带的类和对象: 类名的作用: 类里面的与属性相关的对象self的运用: 实例化:就是创建一个对象 调用方法,类名.方法名(对象名) 执行步骤: 简写:alex.walk()等价于Pe ...

  3. KMP未优化模板、

    要理解KMP最重要的一点就是防止重复的回溯. !!!很重要!!!很重要!!!很重要 要了解KMP可以去:http://www.cnblogs.com/dolphin0520/archive/2011/ ...

  4. Netty进行文件传输

    本次是利用TCP在客户端发送文件流,服务端就接收流,写入相应的文件. 实验的源文件是一个图片,假设地址是D:\\Koala.jpg,接收保存后的图片为D:\\test.jpg 原理就是将文件读取成by ...

  5. SVN提示update更新成功,但是本地文件却没有更新

    问题描述:将仓库的最新版本代码check out到本地后,然后最某个文件做了修改,保存后想通过svn的update来重新得到最新的版本,发现失效. 原因:经过多方查找原因,主要看了以下两篇文档 htt ...

  6. java 九个预定义Class对象

    基本的 Java 类型(boolean.byte.char.short.int.long.float 和 double)和关键字 void通过class属性也表示为 Class 对象: Class类中 ...

  7. 浅谈LOG日志的写法

    文章来源于公司的大牛 1 Log的用途 不管是使用何种编程语言,日志输出几乎无处不再.总结起来,日志大致有以下几种用途: l  问题追踪:通过日志不仅仅包括我们程序的一些bug,也可以在安装配置时,通 ...

  8. java用普通类如何实现枚举功能

    用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能.     1.私有的构造方法.     2.每个元素分别用一个公有的静态成员变量表示.      可以有若干公有方法或抽象方法.采用 ...

  9. 2018百度之星初赛B - A,D,F

    总结:这一次的百度之星之行到这里也就结束了,充分的认识到了自己的不足啊...果然还是做的题太少,,见识的题型也还太少,对于STL的掌握还是不够到位啊!!(STL大法是真的好,建议大家认认真真的好好学学 ...

  10. C# 如何写出一个不能被其他程序集继承的抽象类

    我需要限定某个抽象类只能在我程序集类实现,而不支持其他程序集实现,也就是我需要一个不能被继承的抽象类 在 C# 里面有抽象类和接口,这两个都是期望被继承才能被使用,而抽象类是可以做到只能在自己程序集和 ...