Golang并发模式

时下,golang语言已经变成了最热门的编程语言之一。借助与他高效、静态类型、以及强大的并发性,以及不乏动态语言特性的语法,吸引了大量C、C++、Java、python码农成为他的拥趸。尤其是在容器领域,golang开发的docker更是一枝独秀,风靡于零。今天虫虫就给大家介绍一下golang中最值得称道的并发功能,介绍常见的Go并发模式,本文假设你已经熟悉golang基本语法,有初步的golang编程经验。

我们首先要说的是,任何的并发性,即使使用Go内置的最简单的原语,本质上也很复杂。所以建议Golang初学者尽可能多,多学多用其并发(goroutines和channel),直到熟练掌握该技能。

Golang并发使用建议

最小化并发接口

我们假想,将并发视看成一个Web网页。我们不能让(或至少不应该)ORM(处理数据请求,返回数据)给你返回HTML片段。接口应该以直接的方式编写:参数进来,返回值出来。易于测试,易于推理。绝大多数代码不应与信道(channel)交互或通过协程(goroutine)运行。代码也不应该返回表示延迟或正在进行的工作的回调(callback)。

避免过早优化并发

Go社区喜欢优化,而且很多是不必要的优化。尽管过度优化一般情况都是无害的,但是涉及信道和协程时候,除非有明显的性能改进,否则绝对应该避免过度优化。一个具体的例子是创建任务池时候,使用协程方便快捷,即用即建,不用既弃。为具体小任务创建协程,不要创建长期工作的协程。需要指出的是,即便你调度得当,基于CPU限制,你的协程都可能成为整个程序的瓶颈,如果你确定需要写一个函数,最好给其添加runtime.Gosched()(有地方用time.Sleep(0)))。

利用内置线程安全性

在golang中有一些常用的功能可以安全地在并发中使用。比如写文件,至少在Linx中将信道映射到协程然后写入标准输出(标准错误也类似)是可以的。但因为在文件写方法中文件已经被锁定,这样做是多余的。

并发模式

下面我们介绍一下常见的简单可靠的并发模式。

安全完成

任何使用协程的人都应该非常清楚这一点,协程并不表示完全开箱即用。有几种方法可以在完成时构建阻塞,但是对该模式,使用sync.WaitGroup类型。通常是你在开始和完成协程之前添加waitgroup。下面的第一个例子将展示它是如何工作的。重要的是要记住,如果你是在协程内部添加的话,你可能犯了一个错误。

并发输出

这几乎不能称之为模式,但对于任何并发代码来说都是一个良好的开端:

在上面的代码中,我们使用WaitGroup来阻塞,直到所有未完成的协程都完成。上面使用的另一种模式是令牌通道,用于避免同时运行10个以上的协程。如前所述,这意味着我们不需要一个10 worker的任务池,而是最多运行10个协程。这意味着,当我们加快工作或完成最后剩余的工作时,我们只有那么多的协程,而不是有那么多等待工作的空闲协程。

这个模式的好处是输出会在它准备就绪时出现,这在Unix管道中很好用,或者只是让用户意识到工作是以自然的方式完成的(而不是微调器或类似的东西)主要缺点是输出的顺序几乎完全与底层函数的速度无关。

请特别注意延迟处理协程中的标记和等待组。在使用早期返回添加错误处理时,应该切换到延迟。否则程序将永远挂起。

并发映射

最后是一个更取巧的模式,在 Golang中也随处可见。与上面的模式类似,但每个协程映射到切片内的一个槽。切片可以安全地并行修改。

与并发输出不同,此代码仅在所有数据准备就绪时打印输出,但是对其维护也以相对比较自然。

最后,关于Golang,不管你是否用得到,是一个每一个人都值得尝试的语言,如果你想学习,虫虫在此给你推它荐两本书,一本是go语言圣经《The Go Programming Language》,它不仅仅是一本伟大的Go语言教程,而且是一本优秀的编程书,里面有大量的并发性技巧介绍。

另一本要考虑学习Go的书是Go Programming Blueprints。它具有几乎交互式的风格,可以在其中编写代码,查看语法错误(或其他任何内容),修复代码并进行迭代。

最后请关注虫虫,虫虫会时常推送编程技巧、有用的工具、业界动态等相关内容给大家奉上。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181026A0TPNK00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券