并发:同时做多件事情

多线程:并发的一种形式,它采用多个线程来执行程序。

并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。并行处理是多线程的一种,而多线程是并发的一种。

异步编程:并发的一种形式,它采用 future 模式或回调(callback)机制,以避免产生不必要的 线程,异步编程的核心理念是异步操作:启动了的操作将会在一段时间后完成。这个操作 正在执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当 操作完成时,会通知它的 future,或者调用回调函数,以便让程序知道操作已经结束,

async 和 await:这让异步编程变得几乎和同步(非并发)编程一样容易。await的作用:启动一个将会被执行的Task(该Task将会在新线程中执行),并立即返回,所以await所在的函数不会被阻塞,当Task完成后,继续执行await后面的代码。

 通常情况下,一个并发程序要使用多种技术。大多数程序至少使用了多线程(通过线程 池)和异步编程

异步编程有两大好处。第一个好处是对于面向终端用户的 GUI 程序:异步编程提高了响应 能力。我们都遇到过在运行时会临时锁定界面的程序,异步编程可以使程序在执行任务时 仍能响应用户的输入。第二个好处是对于服务器端应用:异步编程实现了可扩展性。服务 器应用可以利用线程池满足其可扩展性,使用异步编程后,可扩展性通常可以提高一个数 量级。
现代的异步 .NET 程序使用两个关键字:async 和 await。async 关键字加在方法声明上, 它的主要目的是使方法内的 await 关键字生效(为了保持向后兼容,同时引入了这两个关 键字)。如果 async 方法有返回值,应返回 Task<T>;如果没有返回值,应返回 Task。这些 task 类型相当于 future,用来在异步方法结束时通知主程序。

1.Async/Await

async 方法在开始时以同步方式执行。在 async 方法内部,await 关键字 对它的参数执行一个异步等待。它首先检查操作是否已经完成,如果完成了,就继续运行 (同步方式)。否则,它会暂停 async 方法,并返回,留下一个未完成的 task。一段时间后, 操作完成,async 方法就恢复运行。 不会阻塞UI线程 ,await方法完成自动的通知他 然后执行剩余的代码,异步是为了程序本身不卡 调用处还是异步的 调用处下面的代码还是会先执行 await实际上只是把主线程释放了,使用了其他线程代替他继续 异步不是为了让请求更快,而是为了可以处理更多请求

异步有一个核心,是Task。而Task有一个方法,就是Wait,写法是Task.Wait()。所以,很多人把这个Wait和await混为一谈,这是错的。

这个问题来自于Task。C#里,Task不是专为异步准备的,它表达的是一个线程,是工作在线程池里的一个线程。异步是线程的一种应用,多线程也是线程的一种应用。Wait,以及Status、IsCanceled、IsCompleted、IsFaulted等等,是给多线程准备的方法,跟异步没有半毛钱关系。当然你非要在异步中使用多线程的Wait或其它,从代码编译层面不会出错,但程序会。

尤其,Task.Wait()是一个同步方法,用于多线程中阻塞等待。

用Task.Wait()来实现同步方法中调用异步方法,这个用法本身就是错误的。 异步不是多线程,而且在多线程中,多个Task.Wait()使用也会死锁,也有解决和避免死锁的一整套方式。

Task.Wait()是一个同步方法,用于多线程中阻塞等待,不是实现同步方法中调用异步方法的实现方式。

在异步中,await表达的意思是:当前线程/方法中,await引导的方法出结果前,跳出当前线程/方法,从调用当前线程/方法的位置,去执行其它可能执行的线程/方法,并在引导的方法出结果后,把运行点拉回到当前位置继续执行;直到遇到下一个await,或线程/方法完成返回,跳回去刚才外部最后执行的位置继续执行。

2.异步锁

从 .NET Framework 4.5 开始,任何使用 async/await 进行修饰的方法,都会被认为是一个异步方法;实际上,这些异步方法都是基于队列的线程任务,从你开始使用 Task 去运行一段代码的时候,实际上就相当于开启了一个线程,默认情况下,这个线程数由线程池 ThreadPool 进行管理的。

线程安全的访问方式可以通过lock来进行唯一线程限定,但如果使用await等待Task完成,则Task中不允许使用lock。

因此采用另外一种方式完成

调用

3.ConcurrentQueue 队列

锁的引入,带来了一定的开销和性能的损耗,并降低了程序的扩展性,而且还会有死锁的发生(虽说概率不大,但也不能不防啊),因此:使用LOCK进行并发编程显然不太适用。

还好,微软一直在更新自己的东西:

.NET Framework 4提供了新的线程安全和扩展的并发集合,它们能够解决潜在的死锁问题和竞争条件问题,因此在很多复杂的情形下它们能够使得并行代码更容易编写,这些集合尽可能减少使用锁的次数,从而使得在大部分情形下能够优化为最佳性能,不会产生不必要的同步开销。

ConcurrentQueue 队列

ConcurrentQueue 是完全无锁的,能够支持并发的添加元素,先进先出。下面贴代码,详解见注释:

class Program
{
private static object o = new object();
/*定义 Queue*/
private static Queue<Product> _Products { get; set; }
private static ConcurrentQueue<Product> _ConcurrenProducts { get; set; }
/* coder:天才卧龙
* 代码中 创建三个并发线程 来操作_Products 和 _ConcurrenProducts 集合,每次添加 10000 条数据 查看 一般队列Queue 和 多线程安全下的队列ConcurrentQueue 执行情况
*/
static void Main(string[] args)
{
Thread.Sleep(1000);
_Products = new Queue<Product>();
Stopwatch swTask = new Stopwatch();//用于统计时间消耗的
swTask.Start(); /*创建任务 t1 t1 执行 数据集合添加操作*/
Task t1 = Task.Factory.StartNew(() =>
{
AddProducts();
});
/*创建任务 t2 t2 执行 数据集合添加操作*/
Task t2 = Task.Factory.StartNew(() =>
{
AddProducts();
});
/*创建任务 t3 t3 执行 数据集合添加操作*/
Task t3 = Task.Factory.StartNew(() =>
{
AddProducts();
}); Task.WaitAll(t1, t2, t3);
swTask.Stop();
Console.WriteLine("List<Product> 当前数据量为:" + _Products.Count);
Console.WriteLine("List<Product> 执行时间为:" + swTask.ElapsedMilliseconds); Thread.Sleep(1000);
_ConcurrenProducts = new ConcurrentQueue<Product>();
Stopwatch swTask1 = new Stopwatch();
swTask1.Start(); /*创建任务 tk1 tk1 执行 数据集合添加操作*/
Task tk1 = Task.Factory.StartNew(() =>
{
AddConcurrenProducts();
});
/*创建任务 tk2 tk2 执行 数据集合添加操作*/
Task tk2 = Task.Factory.StartNew(() =>
{
AddConcurrenProducts();
});
/*创建任务 tk3 tk3 执行 数据集合添加操作*/
Task tk3 = Task.Factory.StartNew(() =>
{
AddConcurrenProducts();
}); Task.WaitAll(tk1, tk2, tk3);
swTask1.Stop();
Console.WriteLine("ConcurrentQueue<Product> 当前数据量为:" + _ConcurrenProducts.Count);
Console.WriteLine("ConcurrentQueue<Product> 执行时间为:" + swTask1.ElapsedMilliseconds);
Console.ReadLine();
} /*执行集合数据添加操作*/ /*执行集合数据添加操作*/
static void AddProducts()
{
Parallel.For(0, 30000, (i) =>
{
Product product = new Product();
product.Name = "name" + i;
product.Category = "Category" + i;
product.SellPrice = i;
lock (o)
{
_Products.Enqueue(product);
}
}); }
/*执行集合数据添加操作*/
static void AddConcurrenProducts()
{
Parallel.For(0, 30000, (i) =>
{
Product product = new Product();
product.Name = "name" + i;
product.Category = "Category" + i;
product.SellPrice = i;
_ConcurrenProducts.Enqueue(product);
}); }
} class Product
{
public string Name { get; set; }
public string Category { get; set; }
public int SellPrice { get; set; }
}

从执行时间上来看,使用 ConcurrentQueue 相比 LOCK 明显快了很多!

   1.BlockingCollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。

   2.ConcurrentBag 提供对象的线程安全的无序集合

   3.ConcurrentDictionary  提供可有多个线程同时访问的键值对的线程安全集合

   4.ConcurrentQueue   提供线程安全的先进先出集合

   5.ConcurrentStack   提供线程安全的后进先出集合

C# 并发编程 (异步编程与多线程)的更多相关文章

  1. 谈谈C#多线程开发:并行、并发与异步编程

    阅读导航 一.使用Task 二.并行编程 三.线程同步 四.异步编程模型 五.多线程数据安全 六.异常处理 概述 现代程序开发过程中不可避免会使用到多线程相关的技术,之所以要使用多线程,主要原因或目的 ...

  2. C#异步编程の-------异步编程模型(APM)

    术语解释: APM               异步编程模型, Asynchronous Programming Model EAP                基于事件的异步编程模式, Event ...

  3. 转:[你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0——C#4.0中一些 ...

  4. [你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中 介绍了从C#1.0——C#4.0中一 ...

  5. 异步编程系列第02章 你有什么理由使用Async异步编程

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  6. async和await关键字实现异步编程

    async和await关键字实现异步编程 异步编程   概念 异步编程核心为异步操作,该操作一旦启动将在一段时间内完成.所谓异步,关键是实现了两点:(1)正在执行的此操作,不会阻塞原来的线程(2)一旦 ...

  7. 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法

    什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...

  8. 【C# TAP 异步编程】三、async\await的运作机理详解

    [原创] 本文只是个人笔记,很多错误,欢迎指出. 环境:vs2022  .net6.0 C#10 参考:https://blog.csdn.net/brook_shi/article/details/ ...

  9. iOS 为什么app都是异步编程

    iOS 为什么app都是异步编程 对本文题目首先需要了解一下什么是异步编程,异步编程即多线程编程. 多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径. 在具体理解多线程之前先看一个都理 ...

随机推荐

  1. git学习与应用

    git是什么 Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目:同类的有svn,如果知道svn是干嘛的(上篇),那么git是啥就不多解释了. Git 与常用的版本控制工具 C ...

  2. How tomcat works(深入剖析tomcat)servlet容器

    How tomcat works (5)servlet容器阅读笔记 第四章阅读了tomcat默认连接器的实现,当时connector中的使用的容器是自定义的容器,也是非常之简单奥,一个人就干完了所有的 ...

  3. UUID介绍与生成的方法

    什么是UUID? UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符.UUID具有以下涵义: 经由一定的算法 ...

  4. 三万字无坑搭建基于Docker+K8S+GitLab/SVN+Jenkins+Harbor持续集成交付环境

    写在前面 最近在 K8S 1.18.2 版本的集群上搭建DevOps环境,期间遇到了各种坑.目前,搭建环境的过程中出现的各种坑均已被填平,特此记录,并分享给大家! 文章和搭建环境所需要的yml文件已收 ...

  5. Spark SQL解析查询parquet格式Hive表获取分区字段和查询条件

    首先说一下,这里解决的问题应用场景: sparksql处理Hive表数据时,判断加载的是否是分区表,以及分区表的字段有哪些?再进一步限制查询分区表必须指定分区? 这里涉及到两种情况:select SQ ...

  6. Plant Leaves Classification植物叶子分类:基于孪生网络的小样本学习方法

    目录 Abstract Introduction PROPOSED CNN STRUCTURE INITIAL CNN ANALYSIS EXPERIMENTAL STRUCTURE AND ALGO ...

  7. Jmeter介绍、下载及配置

    Jmeter介绍: Apache JMeter是Apache组织开发的基于Java的压力测试工具.最初被设计用于Web应用测试,后来扩展到其他测试领域. 它可以用于测试静态和动态资源,例如静态文件.J ...

  8. 第2章 Python编程基础知识 第2.1节 简单的Python数据类型、变量赋值及输入输出

    第三节 简单的Python数据类型.变量赋值及输入输出 Python是一门解释性语言,它的执行依赖于Python提供的执行环境,前面一章介绍了Python环境安装.WINDOWS系列Python编辑和 ...

  9. PyQt Designer中带参数的信号为什么匹配不到带参数的槽函数?

    老猿在学习ListView组件时,想实现一个在ListView组件中选中一个选择项后触发消息给主窗口,通过主窗口显示当前选中的项的内容. 进入QtDesigner后,设计一个图形界面,其中窗口界面使用 ...

  10. PyQt(Python+Qt)学习随笔:Qt Designer中主窗口对象的tabShape属性

    tabShape属性用于控制主窗口标签部件(Tab Widget)中的标签的形状,对应类型为QTabWidget.TabShape,有两种取值: 1.QTabWidget.Rounded:对应值为0, ...