【FAQ】go的十万个为什么?-并发|Go主题月

522 阅读4分钟

本文中译于官方FAQ

原文地址:golang.org/doc/faq#Con…

Go项目并发

什么是原子操作?什么是互斥锁?

可以在 Go 内存模型文章看到关于原子操作的描述。

syncsync/atomic 包中提供了较低等级的同步和原子基元。这些包适用与一些简单的任务,比如引用计数的自增或保证小规模的互斥行为。

Go 通过协程和 channel 来实现更高级的操作,比如并发服务器之间的协调等,高级技术能带来更好的程序。例如,你可以构建一个程序,这个程序的一个协程只负责某个特定的数据。最初的 Go proverb 概括了这种方法,

不要通过共享内存进行通信。而是通过通信共享内存。

有关此概念的详细讨论,请参见通过通信共享内存 代码步及其相关文章。

大型并发程序可能会从这两个工具箱中借用。

为什么我的程序在使用更多CPU时不能更快地运行?

程序是否在使用更多CPU的情况下运行速度更快,取决于所要解决的问题。Go语言提供并发原语,例如协程和 channel,但是并发仅在基本问题本质上是并行的时才启用并行。本质上是顺序性的问题不能通过添加更多的CPU来加速,而那些可以分解为可以并行执行的碎片的速度可以加快,有时甚至可以大大提高。

有时添加更多的CPU可能会减慢程序速度。实际上,与使用有用的计算相比,花费更多时间进行同步或通信的程序可能会在使用多个OS线程时出现性能下降的情况。这是因为在线程之间传递数据会涉及切换上下文,这会带来巨大的成本,并且随着CPU数量的增加,这种成本会增加。例如,尽管Go规范中的主筛示例启动了许多goroutine,但它们并没有明显的并行性。增加线程(CPU)的数量可能会减慢其速度,而不是加快速度。

有关此主题的更多详细信息,请参见标题为并发不是并行性的主题。

如何控制CPU的数量?

可同时用于执行 goroutine 的 CPU 数量由GOMAXPROCS shell环境变量控制,默认值是 CPU 的个数。因此,具有并行执行潜能的程序应默认在多CPU机器上实现。要更改要使用的并行CPU的数量,请设置环境变量或使用运行时包的名称相似的功能来配置运行时支持以使用不同数量的线程。将其设置为1消除了真正的并行性的可能性,从而迫使独立的goroutine轮流执行。

运行时可以分配比GOMAXPROCS值更多的线程来服务多个未完成的I/O请求。GOMAXPROCS仅影响一次可以实际执行多少个 goroutine。系统调用中可能会任意阻止更多内容。

Go 的 goroutine 调度程序虽然随着时间的推移已得到改进,但并没有达到所需的效果。将来,它可能会更好地优化对 OS线程 的使用。目前,如果存在性能问题,则按应用程序设置GOMAXPROCS可能会有所帮助。

为什么没有协程ID?

协程没有名字,它们是匿名的。它们不会向程序员公开任何唯一的标识符,名称或数据结构。一些人可能会为此感到惊讶,期望 go 语句返回一些可用于以后访问和控制 goroutine 的项目。

goroutines 匿名的根本原因是,在编写并发代码时,可以使用完整的Go语言。相比之下,在命名线程和 goroutine 时开发的使用模式可能会限制使用它们的库可以做什么。

这是困难的例证。一旦命名了一个 goroutine 并在其周围构建了一个模型,它将变得很特别,并且人们倾向于将所有计算与该 goroutine 相关联,而忽略了使用多个可能共享的 goroutine 进行处理的可能性。如果net/http包将每个请求状态与 goroutine 关联,则客户端在处理请求时将无法使用更多 goroutine。

而且,对于诸如图形系统的那些需要在“主线程”上进行所有处理的库的经验表明,当以并发语言部署时,该方法有多么尴尬和局限性。特殊线程或 goroutine 的存在迫使程序员扭曲程序,以避免因错误地对错误的线程进行操作而导致崩溃和其他问题。

对于那些特定的 goroutine 确实很特殊的情况,该语言提供了诸如 channel 之类的功能,可以灵活地使用它们与之交互。