我为什么喜欢用C#来做并发编程

并发编程的术语

  • 并发
    同时做多件事情
  • 多线程
    并发的一种形式,它采用多个线程来执行程序。
    多线程是并发的一种形式,但不是唯一的形式。
  • 并行处理
    把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。
    并行处理是多线程的一种,而多线程是并发的一种。
  • 异步编程
    并发的一种形式,它采用future模式或回调(callback)机制,以避免产生不必要的线程。
    一个 future(或 promise)类型代表一些即将完成的操作。在 .NET
    中,新版 future 类型有 Task 和 Task 。在老式异步编程 API
    中,采用回调或事件(event),而不是future。异步编程的核心理念是异步操作(asynchronous
    operation)
    :启动了的操作将会在一段时间后完成。这个操作正在执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当操作完成时,会通知它的
    future,或者调用回调函数,以便让程序知道操作已经结束。
  • 响应式编程
    一种声明式的编程模式,程序在该模式中对事件做出响应。
    响应式编程的核心理念是异步事件(asynchronous
    event)
    :异步事件可以没有一个实际的“开始”,可以在任何时间发生,并且可以发生多次,例如用户输入。
    如果把一个程序看作一个大型的状态机,则该程序的行为便可视为它对一系列事件做出响应,即每换一个事件,它就更新一次自己的状态。

在并发编程中我们经常听到以下一些概念,今天我将尝试进行阐述。

(此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注。)

异步编程的两个好处

  1. 对于面向终端用户的 GUI
    程序:异步编程提高了响应能力。面对在运行时被临时锁定界面的程序,异步编程可以使程序在此时仍能流畅的响应用户的输入。譬如:WPF界面,执行一个需要等待的操作时,仍可以点击输入框进行填写,而不会出现卡顿,无法点击的情况或者对页面无法进行拖拽。
  2. 对于服务器端应用:异步编程实现了可扩展性。服务器应用可以利用线程池满足其可扩展性,使用异步编程后,可扩展性通常可以提高一个数量级。即提高服务器端应用的TPS(Transactions
    Per Second)和 QPS (Queries Per Second)

一、并发

题记:就语言和运行时层面,C#做并发编程一点都不弱,缺的是生态和社区。

并行的两种形式

并行编程的使用场景:需要执行大量的计算任务,并且这些任务能分割成相互独立的任务块儿

并行的形式有两种:数据并行(data parallelism)和任务并行(task
parallelim)。

数据并行(data
parallelism):有大量的数据需要处理,并且每一块数据的处理过程基本上是彼此独立的。

任务并行(task
parallelim):需要执行大量任务,并且每个任务的执行过程基本上是彼此独立的。任务并行可以是动态的,如果一个任务的执行结果会产生额外的任务,这些新增的任务也可以加入任务池。

实现数据并行的方法

  • Parallel.ForEach
  • PLINQ(Parallel LINQ)

每个任务块要尽可能的互相独立。
只要任务块是互相独立的,并行性就能做到最大化。一旦你在多个线程中共享状态,就必须以同步方式访问这些状态,那样程序的并行性就变差了。

数据并行重点在处理数据,任务并行则关注执行任务。

实现任务并行的方法

  • Parallel.Invoke
  • Task.Wait

通常情况下,没必要关心线程池处理任务的具体做法。数据并行和任务并行都使用动态调整的分割器,把任务分割后分配给工作线程。线程池在需要的时候会增加线程数量。线程池线程使用工作窃取队列(work-stealing
queue)。

同时干多件事情,这就是并发的作用。

硅谷才女朱赟(我的家门)昨天发了一篇文章《为什么用 Java ——
关于并发编程》,让大家学习了Java中如何进行并发编程的一些基本知识。作为一个将近15年的.NET程序员,我觉得有必要给大家补充介绍一下C#进行并发编程的知识(当然不会太深入讲解)。这篇文章无意进行技术比较,毕竟技术只是工具(大同小异,各有千秋),主要还是看用工具的人。

响应式编程Rx学习难度较大

使用场景:处理的事件中带有参数,最好采用响应式编程
响应式编程的核心概念是:可观察的流(observable stream)
响应式编程的最终代码非常像 LINQ,可以认为它就是“LINQ to
events”,它采用“推送”模式,事件到达后就自行穿过查询。

web服务器可以利用并发同时处理大量用户的请求。

并发(英文Concurrency),其实是一个很泛的概念,字面意思就是“同时做多件事”,不过方式有所不同。在.NET的世界里面,并发一般涉及如下几个方面:

TPL数据流

异步编程和并行编程这两种技术结合起来就是TPL数据流
数据流网格的基本组成单元是数据流块(dataflow block)。

Rx 和 TPL有很多相同点。
网格和流都有“数据项”这一概念,数据项从网格或流的中间穿过。还有,网格和流都有“正常完成”(表示没有更多数据需要接收时发出的通知)和“不正常完成”(在处理数据中发生错误时发出的通知)这两个概念。但是,Rx
和 TPL 数据流的性能并不相同。

当需要执行需要计时的任务,最佳选择是Rx的 可观察流 observable 对象
国际太阳娱乐网站2138 ,当需要进行并行处理,最佳选择是 TPL数据流块

只要我们需要程序同时干多件事情,我们就需要并发。

  1. 多线程编程(已过时,不介绍)
  2. 异步编程
  3. 并行编程
  4. 响应式编程
  5. 数据流编程

线程和线程池

线程是一个独立的运行单元,每个进程内部有多个线程,每个线程可以各自同时执行指令。每个线程有自己独立的栈,但是与进程内的其他线程共享内存。
对某些程序来说,其中有一个线程是特殊的,例如用户界面程序有一个 UI
线程,控制台程序有一个 main 线程。

每个 .NET
程序都有一个线程池,线程池维护着一定数量的工作线程,这些线程等待着执行分配下来的任务。线程池可以随时监测线程的数量。配置线程池的参数多达几十个,但是建议采用默认设置,线程池的默认设置是经过仔细调整的,适用于绝大多数现实中的应用场景。

二、多线程

为了支持以上编程,.NET提供了很多基础功能,比如:委托,匿名函数,Lambda表达式,线程池,Task模型,支持并发的集合(线程安全集合和不可变集合)
,调度器,同步功能。在这里,就不对这些内容进行介绍了,大家可以自行搜索学习。另外,对于Actor模型,.NET中也有支持,但我不认为它属于语言/运行时层面的并发,它更像架构层面的并发,我最后会简单介绍。

并发编程的设计原理

大多数并发编程技术有一个类似点:它们本质上都是函数式(functional)的。函数式编程理念是并发编程的本质。

并发编程的一种形式,其采用多个线程执行程序。

1,异步编程

异步编程就是使用future模式(又称promise)或者回调机制来实现(Non-blocking
on waiting)。

如果使用回调或事件来实现(容易callback
hell),不仅编写这样的代码不直观,很快就容易把代码搞得一团糟。不过在.NET
4.5(C# 5)中引入的async/await关键字(在.NET
4.0中通过添加Microsoft.Bcl.Async包也可以使用),让编写异步代码变得容易和优雅。通过使用async/await关键字,可以像写同步代码那样编写异步代码,所有的回调和事件处理都交给编译器和运行时帮你处理了。

使用异步编程有两个好处:不阻塞主线程(比如UI线程),提高服务端应用的吞吐量。所以微软推荐ASP.NET中默认使用异步来处理请求。

要详细了解异步编程,可以参考官方文档:和《Async
in C#
5.0》这本书。另外,在这个官方文档中,微软还特意把异步编程分作了3种不同的模型:基于任务的模式(TAP)就是我上面推荐的这种,基于事件的模式(EAP)和异步编程模型(APM)我上面不推荐的事件和回调。

线程是一个独立的运行单元,每个进程内部有多个线程,每个线程可以各自同时执行指令。

2,并行编程

并行编程的出现实际上是随着CPU有多核而兴起的,目的是充分利用多核CPU的计算能力。并行编程由于会提高CPU的利用率,更适合客户端的一些应用,对于服务端的应用可能会造成负面影响(因为服务器本身就具有并行处理的特点,比如IIS会并行的处理多个请求)。我自己使用并行编程最多的场景是之前分析环境数据不确定度的时候,使用并行的方式计算蒙特卡洛模拟(计算上千次之后拟合),当然后来我使用泰勒级数展开来计算不确定度,没有这么多的计算量就无需并行了。当然在计算多方案结果比较的情况下,还是继续使用了并发计算。

在.NET中,并行的支持主要靠.NET
4.0引入的任务并行库和并行LINQ。通过这些库可以实现数据并行处理(处理方式相同,输入数据不同,比如我上面提到的应用场景)或者任务并行处理(处理方式不同,且数据隔离)。通过使用并行处理库,你不用关心Task的创建和管理(当然更不用说底层的线程了),只需要关注处理任务本身就行了。

具体的用法还是参考官方文档:,当然《Parallel
Programming with Microsoft .NET》这本书也行。

每个线程有自己独立的栈,但是与进程内的其他线程共享内存。

3,响应式编程

响应式编程最近成为了一个Buzzword,其实微软6年前就开始给.NET提供一个Reactive
Extensions了。一开始要理解响应式编程有点困难,但是一旦理解了,你就会对它的强大功能爱不释手。简单来说,响应式编程把事件流看作数据流,不过数据流是从IEnumable中拉取的,而事件流是从IObservable推送给你的。为什么响应式编程可以实现并发呢?这是因为Rx做到线程不可知,每次事件触发,后续的处理会从线程池中任意取出一个线程来处理。且可以对事件设置窗口期和限流。举个例子,你可以用Rx来让搜索文本框进行延迟处理(而不用类似我很早的时候用个定时器来延迟了)。

要详细了解Rx最好的方式就是浏览 IntroToRx.com
这个网站,当然还有官方文档:。

线程池是线程更广泛的一种应用形式,其维护着一定数量的工作线程,这些线程等待着执行分配下来的任务。线程池可以随时监测线程的数量

4,数据流编程

数据流(DataFlow)编程可能大家就更陌生了,不过还是有些常用场景可以使用数据流来解决。数据流其实是在任务并行库(TPL)上衍生出来的一套处理数据的扩展(也结合了异步的特性),TPL也是处理并行编程中任务并行和数据并行的基础库。

望文生义,TPL
DataFlow就是对数据进行一连串处理,首先为这样的处理定义一套网格(mesh),网格中可以定义分叉(fork)、连接(join)、循环(loop)。数据流入这样的处理网格就能够并行的被处理。你可以认为网格是一种升级版的管道,实际上很多时候就是被当作管道来使用。使用场景可以是“分析文本文件中词频”,也可以是“处理生产者/消费者问题”。

参考资料当然也是官方文档:。

线程池催生了另外一种重要的并发形式:并行处理。

5,Actor模型

Scala有Akka,其实微软研究院也推出了Orleans来支持了Actor模型的实现,当然也有Akka.NET可用。Orleans设计的目标是为了方便程序员开发需要大规模扩展的云服务,
可用于实现DDD+EventSourcing/CQRS系统。

官方网站是:,善友也有介绍:

那么,我为什么喜欢使用C#来做并发编程呢?显而易见,有上面这些唾手可得的工具,使用C#同样可以轻易开发并发程序。

多线程并不是并发编程的唯一形式,虽然.NET和Java等语言框架都对底层线程类型提供了支持,但是对开发人员并不友好,最新的.NET和Java

都提供了更高级别的抽象,让我们开发并发程序更加方便高效。

三、并行处理

将大块的任务分割成相互独立的小块,并分配给多个同时运行的线程处理。

并行处理采用多线程,提高了处理器的利用效率。

并行编程通常不适合服务器系统,服务器本身都具有并发处理能力。

数据并行可以处理大量的彼此独立的数据,比如Hadoop等大数据处理框架。

任务并行可以将彼此独立的拆分任务同时执行。

下边看下.NET中提供的并行编程

使用Parallel.ForEach进行数据并行

void RotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
    Parallel.ForEach(matrices, matrix => matrix.Rotate(degrees));
}

 

使用Parallel.ForEach进行数据并行

IEnumerable<bool> PrimalityTest(IEnumerable<int> values)
{
    return values.AsParallel().Select(val => IsPrime(val));
}

 

数据的独立性是并行性最大化的前提,否为了确保安全性就需要引入同步,从而影响程序的并行程度。

只能最大程度的并行,但是总是消灭不了同步,数据并行的结果总是需要进行聚合,Parallel实现了响应的重载及map/reduce函数。

Parallel类的Invoke方式可以实现任务并行

国际太阳娱乐网站2138 1

void ProcessArray(double[] array)
{
    Parallel.Invoke(
        () => ProcessPartialArray(array, 0, array.Length / 2),
        () => ProcessPartialArray(array, array.Length / 2, array.Length)
    );
}
void ProcessPartialArray(double[] array, int begin, int end)
{
    // CPU 密集型的操作......
}        
You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图