代码之家  ›  专栏  ›  技术社区  ›  BornToCode

TPL数据流与普通信号量

  •  3
  • BornToCode  · 技术社区  · 6 年前

    我需要一个可伸缩的过程。进程主要有I/O操作和一些次要的CPU操作(主要是反序列化字符串)。该过程向数据库查询url列表,然后从这些url获取数据,将下载的数据反序列化为对象,然后将一些数据持久化到crm dynamics以及其他数据库。之后,我需要更新第一个数据库的网址处理。部分需求是使并行度可配置。

    最初,我想通过一系列带有await的任务来实现它,并使用信号量限制并行性—非常简单。然后我阅读了@Stephen Cleary的一些帖子和答案,其中推荐使用TPL数据流,我认为它可能是一个很好的候选人。不过,我想确保我是“复杂”的代码使用数据流为一个有价值的原因。我还有个建议 ForEachAsync extension method 使用起来也很简单,但是我不确定它是否会因为分割集合的方式而导致内存开销。

    对于这种情况,TPL数据流是一个好的选择吗?它如何比信号量或ForEachAsync方法更好-如果我通过TPL数据流在其他每个选项(信号量/ForEachAsync)上实现它,我实际上会获得什么好处?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Stephen Cleary    6 年前

    进程主要有IO操作和一些次要的CPU操作(主要是反序列化字符串)。

    那几乎就是I/O。除非那些字符串 巨大的 ,反序列化不值得并行化。你正在做的那种CPU工作将在噪音中丢失。

    所以,您需要关注并发异步。

    • SemaphoreSlim 是这个的标准模式,正如你所发现的。
    • TPL数据流还可以执行并发(异步和并行形式)。

    ForEachAsync 可以有几种形式;注意 blog post 你提到的,有 此方法的不同实现,每个实现都是有效的。”这里有许多不同的语义可以用于迭代,每种语义都会导致不同的设计选择和实现 Task.Run 或者分割。在异步并发世界中,任何 前异步 实现只是一种语法糖,隐藏了它实现的语义,这就是为什么我倾向于避免它。

    这会让你 信号灯 与。 ActionBlock . 我通常建议人们从 信号灯 首先,如果他们的需求变得更加复杂(在某种程度上,他们似乎会从数据流管道中受益),那么考虑迁移到TPL数据流。

    例如,“部分要求是使并行度可配置。”

    您可以从允许一定程度的并发开始—其中被限制的是一个单独的完整操作(从url获取数据,将下载的数据反序列化到对象,持久化到crm dynamics和另一个数据库,并更新第一个数据库)。这里就是 信号灯 会是一个完美的解决方案。

    但是,您可能会决定要有多个旋钮:例如,一个级别的并发用于下载多少个url,另一个级别的并发用于持久化,另一个级别的并发用于更新原始数据库。然后,您还需要限制这些点之间的“队列”:内存中只有这么多的反序列化对象,等等,以确保具有慢速数据库的快速url不会导致应用程序使用过多内存出现问题。如果这些都是有用的语义,那么您已经开始从数据流的角度来处理这个问题,这一点是,使用类似于TPL数据流的库可能会更好地为您服务。