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. jq动画和停止动画

    使用jq 实现动画循环效果 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  2. 深入Java线程管理(三):线程同步

    一. 引入同步: 有一个很经典的案例,即银行取款问题.我们可以先看下银行取款的基本流程: 1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. 2)用户输入取款金额. 3)系统判断账户金额是否大 ...

  3. Vue进阶

    组件深入 过渡&动画 可复用性&组合 工具&规模化&内在 ****************参考*************** vue官方教程

  4. uni-app 常用框架内置方法 更新中 .....

    获取 登录信息,getStorage 初始化页面数据   请求  下拉刷新页面 加载更多  点击跳转  个人中心  uni.request(OBJECT)   success=成功    fail=失 ...

  5. Django入门1--Django是什么?Django里文件的作用?

    Django项目目录介绍: wsgi.py文件介绍: urls.py文件介绍: __init__.py文件介绍:

  6. jquery超级简单的后台系统自适应框架

    系统后台自适应简单框架 <!DOCTYPE HTML> <html lang="zh-CN"> <head> <meta http-equ ...

  7. gitLab操作规范和项目流程

    刚做完一个项目并且艰难得上线,对整个项目流程和gitLab规范 有了一些心得,给新来的同学普及一下. 最先产品会写一篇需求文档,咱们要先看需求文档对项目有一个大致了解,然后产品喊后端.ui.前端  一 ...

  8. dotnet core 使用 GBK 编码

    本文告诉大家如何在 .NET Core 中使用 GBK 编码 默认的 .NET Core 框架不包含 GBK 编码,不包含除了代码页为 28591 和 Unicode(utf-8,utf-16) 之外 ...

  9. python实现单词本功能

    #实现简单的单词本:# 可以添加单词和词义,当所添加的单词已经存在 让用户知道:# 查找单词,单词不存在时,让用户知道# 删除单词,当删除的单词不存在时,让用户知道# 以上功能无限制操作,直到用户输入 ...

  10. 无法加载文件C:\Users\TANG\AppData\Roaming\npm\nrm.ps1,因为在此系统上禁止运行脚本

    # 碰到问题解决过程 > 1. 刚在一个新的机器上装node环境时,要用一个nrm管理镜像源时.报了一个错,如图1 ![图1](https://img2018.cnblogs.com/blog/ ...