Codewalk: Share Memory By Communicating

Pop Out Code
左侧右侧的代码 码宽70% filepaths shownhidden
Introduction
Go的并发方法不同于线程和共享内存的传统用法. 从哲学上讲,可以总结为:



通道允许您在goroutine之间传递对数据结构的引用. 如果您认为这是对数据所有权(读取和写入数据的能力)的传递,那么它们将成为功能强大且表现力强的同步机制.

在此代码漫游中,我们将看一个简单的程序,该程序轮询URL列表,检查其HTTP响应代码并定期打印其状态.
doc/codewalk/urlpoll.go
状态类型
状态类型表示URL的状态.

轮询器将State值发送到StateMonitor,该StateMonitor维护每个URL当前状态的映射.
doc/codewalk/urlpoll.go:26,30
资源类型
资源表示要轮询的URL的状态:URL本身以及自上次成功轮询以来遇到的错误数.

程序启动时,它将为每个URL分配一个资源. 主goroutine和Poller goroutine在通道上将资源相互发送.
doc/codewalk/urlpoll.go:60,64
系柱功能
每个轮询器都从输入通道接收资源指针. 在此程序中,约定是在通道上发送资源指针将基础数据的所有权从发送方传递到接收方. 由于这个约定,我们知道没有两个goroutine将同时访问此Resource. 这意味着我们不必担心锁定以防止同时访问这些数据结构.

轮询器通过调用其轮询方法来处理资源.

它将状态值发送到状态通道,以将轮询结果通知给StateMonitor.

最后,它将资源指针发送到输出通道. 这可以解释为轮询器说"我已经完成了此资源",并将其所有权返回给主goroutine.

多个goroutine运行Poller,并行处理资源.
doc/codewalk/urlpoll.go:86,92
投票法
(资源类型的)轮询方法对资源的URL执行HTTP HEAD请求,并返回HTTP响应的状态码. 如果发生错误,则轮询会将消息记录为标准错误,然后返回错误字符串.
doc/codewalk/urlpoll.go:66,77
主功能
main函数启动Poller和StateMonitor goroutine,然后在适当的延迟后循环将完成的资源传递回挂起的通道.
doc/codewalk/urlpoll.go:94,116
建立频道
首先,main生成* Resource的两个渠道,即未决和完成.

在main内部,一个新的goroutine将每个URL发送一个资源到待处理状态,而main goroutine从complete接收已完成的Resources.

待处理和完整的通道将传递到每个Poller goroutine,在其中称为进出.
doc/codewalk/urlpoll.go:95,96
初始化StateMonitor
StateMonitor将初始化并启动一个存储每个资源状态的goroutine. 稍后我们将详细介绍此功能.

现在,要注意的重要一点是它返回状态通道,该通道保存为状态并传递给Poller goroutines.
doc/codewalk/urlpoll.go:98,99
启动Poller goroutines
现在,它具有必要的通道,main启动了许多Poller goroutine,将这些通道作为参数传递. 这些通道提供了main,Poller和StateMonitor goroutine之间的通信方式.
doc/codewalk/urlpoll.go:101,104
将资源发送到待处理
为了向系统中添加初始工作,main启动了一个新的goroutine,该例程将每个URL分配并发送一个资源到待处理.

新的goroutine是必需的,因为未缓冲的通道发送和接收是同步的. 这意味着这些通道的发送将一直阻塞,直到轮询器准备好从挂起状态读取.

如果这些发送在主goroutine中以少于通道发送的Poller进行,程序将陷入死锁状态,因为main尚未从完整接收.

读者的练习:修改程序的这一部分,以从文件中读取URL列表. (您可能希望将此goroutine移到其自己的命名函数中.)
doc/codewalk/urlpoll.go:106,111
主赛事循环
当轮询器完成资源时,它将在完整通道上发送它. 该循环从完成接收那些资源指针. 对于每个接收到的资源,它都会启动一个新的goroutine来调用资源的Sleep方法. 为每个程序使用新的goroutine确保睡眠可以并行发生.

注意,任何单个资源指针都只能在未决状态或完成时发送. 这样可以确保资源是由Poller goroutine处理还是正在睡眠,但绝不能同时进行. 通过这种方式,我们通过交流共享资源数据.
doc/codewalk/urlpoll.go:113,115
睡眠方法
睡眠会调用时间.在发送资源完成之前,睡眠会暂停. 暂停将是固定长度(pollInterval)加上与顺序错误数(r.errCount)成比例的附加延迟.

这是典型的Go习惯用法的一个示例:打算在goroutine中运行的函数接收一个通道,并在该通道上发送其返回值(或完成状态的其他指示).
doc/codewalk/urlpoll.go:79,84
StateMonitor
StateMonitor在通道上接收状态值,并定期输出程序正在轮询的所有资源的状态.
doc/codewalk/urlpoll.go:32,50
更新频道
变量更新是一个状态通道,轮询程序在其上发送状态值.

该通道由函数返回.
doc/codewalk/urlpoll.go:36
urlStatus映射
变量StatStatus是URL到其最新状态的映射.
doc/codewalk/urlpoll.go:37
股票行情
time.Ticker是一个对象,它以指定的间隔在通道上重复发送值.

在这种情况下,自动报价器每隔updateInterval纳秒触发一次当前状态到标准输出的打印.
doc/codewalk/urlpoll.go:38
StateMonitor goroutine
StateMonitor will loop forever, selecting on two channels: ticker.C and update. The select statement blocks until one of its communications is ready to proceed.

When StateMonitor receives a tick from ticker.C, it calls logState to print the current state. When it receives a State update from updates, it records the new status in the urlStatus map.

Notice that this goroutine owns the urlStatus data structure, ensuring that it can only be accessed sequentially. This prevents memory corruption issues that might arise from parallel reads and/or writes to a shared map.
doc/codewalk/urlpoll.go:39,48
Conclusion
在此代码漫步中,我们探索了一个简单的示例,该示例使用Go的并发原语通过通信共享内存.

这应该提供一个起点,从中可以探索使用goroutines和通道来编写表达性和简洁的并发程序的方式.
doc/codewalk/urlpoll.go

by  ICOPY.SITE