并发:同时做多件事情

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

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

异步编程:并发的一种形式,它采用 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. 基于gin的golang web开发:永远不要相信用户的输入

    作为后端开发者我们要记住一句话:"永远不要相信用户的输入",这里所说的用户可能是人,也可能是另一个应用程序."永远不要相信用户的输入"是安全编码的准则,也就是说 ...

  2. 简化的鸿蒙WiFi接口,仅需几行代码,简单易用!

    使用鸿蒙原始WiFI API接口进行编程,整个过程稍显繁琐,为此我们对鸿蒙原始WiFi API接口做了一层封装,形成了一套更简单易用的接口. 简化后的API接口 STA模式 // 连接WiFi热点,并 ...

  3. Python变量引用

    >>>a=3 >>>b=a >>>a=4 >>>b >>>3 >>>List1=[1,2,3 ...

  4. P5656 【模板】二元一次不定方程(exgcd)

    还不会 exgcd 的请移步窝的学习笔记,这里只讲怎么搞出烦人的答案. 在 \(a,b\) 两者互质的情况下,二元一次不定方程的通解:\(a(x+db)+b(y+da)=c\). 所以要先将 \(a, ...

  5. Java基础教程——异常处理详解

    异常处理 好程序的特性 可重用性 可维护性 可扩展性 鲁棒性 |--|--Robust的音译 |--|--健壮.强壮之意 |--|--指在异常和危险情况下系统依然能运行,不崩溃 Java中,写下如下代 ...

  6. IB卡开启EIPOIB做虚拟化桥接使用

    步骤1:开启EiPOIB 编辑默认"no"为"yes" /etc/infiniband/openib.conf E_IPOIB_LOAD=yes 步骤2:重启h ...

  7. Java String 演进全解析

    前言 String 是我们使用最频繁的对象,使用不当会对内存.程序的性能造成影响,本篇文章全面介绍一下 Java 的 String 是如何演进的,以及使用 String 的注意事项. 下面的输出结果是 ...

  8. 饱含辛酸开发 WPF CustomControl

    引言 不知不觉间WPF开发已有两年光景,或许有很多人会问WPF还需要学习吗?WPF还有前途吗?其实我也很担心这个问题. .Net Core3.x已经支持WPF开发,.Net 5也宣布要支持WPF.是否 ...

  9. PyQt(Python+Qt)学习随笔:QTreeWidget中给树型部件增加顶层项的方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTreeWidget对象创建后,是没有任何项的,要给部件增加项,首先要增加顶层项.顶层项的增加有三 ...

  10. PyQt(Python+Qt)学习随笔:Qt Designer中部件的调色板palette属性和字体font属性设置

    一.调色板 在Qt Designer的部件属性中,有个部件调色板(palette)的属性,进入后,如下图所示: 1.调色板palette Qt中提供的调色板palette用于管理控件的外观显示,对应P ...