Concurrent
Go concurrent structure as part of the core language. This section describes some examples of courses and demonstrated their use.
Go write authors group, Go-zh group translation.
https://tour.go-zh.org/concurrency/1
- Go as
Go Cheng (goroutine) is managed by the Go run when lightweight threads.
go f(x, y, z)
We will start a new process and executes Go
f(x, y, z)
f
, x
, y
And z
evaluated occur in the current process of Go, and f
the execution take place in the new Go process.
Go away run in the same address space, it must be synchronized when accessing shared memory. [[ Https://go-zh.org/pkg/sync/ ] [ sync
]] package provides this capability, but it is not often used in Go, because there are other ways (see next page).
// +build OMIT
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
- Nobumichi
Channel is a pipe with a type, you can use the channel through which the operator <-
is transmitted or received values.
ch <- v // 将 v 发送至信道 ch。
v := <-ch // 从 ch 接收值并赋予 v。
( "Arrow" is the direction of data flow.)
And maps and sections, like the channel must be created before using:
ch := make(chan int)
By default, send and receive operations are blocked until the other end is ready. This process can be synchronized such that Go without explicit lock variable or race conditions.
The following example summing the number of sections, assign tasks to process two Go. Once the process is complete their Go two calculations, it can be calculated from the final result.
// +build OMIT
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 将和送入 c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 c 中接收
fmt.Println(x, y, x+y)
}
- Channel Buffered
Channel may be buffered . The length of the buffer as the second parameter provided to the make
channel buffer is initialized with:
ch := make(chan int, 100)
Only when the channel buffer is full, data will be transmitted thereto obstruction. When the buffer is empty, the recipient will be blocked.
Modify the sample to fill the buffer, and then see what happens.
// +build OMIT
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
- range 和 close
The sender can close
be transmitted to a value indicating no need to turn off one channel. The recipient can be tested whether a channel is closed by an expression assigned to receive a second argument: if there is no value can be received and the channel has been closed, then executing the
v, ok := <-ch
At this point ok
it will be set false
.
Cyclic for
I :=
Range c
constantly receives values from the channel, until it is closed.
Note: Only the sender can close the channel, and the receiver can not. Transmitting data to a channel that has been closed the program will cause panic (panic).
Also note: different channels and files, usually without having to close them. Only when it is necessary to tell the recipient no longer has value only needs to transmit necessary to close, such as termination of a range
cycle.
// +build OMIT
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
- select statement
select
Go to make a statement process can wait a plurality of communication operations.
select
Clog to a branch can continue to perform, at which time it will execute the branch. When multiple branches are ready randomly selects an execution.
// +build OMIT
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
- The default selection
When select
the time in other branches are not ready, default
the branch will be executed.
In order not to clog occurs while trying to send or receive, using default
branches:
select {
case i := <-c:
// 使用 i
default:
// 从 c 中接收会阻塞时执行
}
// +build OMIT
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
- Exercise: equivalent binary search tree
The same sequence of values can be stored on a different leaf node of a binary tree. For example, the following sequence of two binary trees are saved 1,1,2,3,5,8,13
.
.image /content/img/tree.png
In most languages, to check whether two binary trees to save the same sequence of functions are quite complicated.
We will use Go's concurrency and channel to write a simple solution.
This embodiment uses a tree
package, which defines the type:
type Tree struct {
Left *Tree
Value int
Right *Tree
}
Click [[ JavaScript: the Click ( '.next-Page')] [next page]] to continue.
- Exercise: equivalent binary search tree
1. implement Walk
functions.
2. Test Walk
function.
Function is tree.New(k)
used to construct a random structure sorted binary search tree, it holds the value of k
, 2k
, 3k
, ..., 10k
.
Create a new channel ch
and its stepper:
go Walk(tree.New(1), ch)
10 and then reads the values from the print channel. It should be a number 1,
2, 3,
..., 10
.
3. used Walk
to achieve Same
the function to detect t1
and t2
whether to store the same values.
4. The test Same
function.
Same(tree.New(1),
tree.New (. 1)) 应当返回
to true ,而
Same, (tree.New (. 1), tree.New(2))
should be returned false
.
Tree
The documents are available on [[ https://godoc.org/golang.org/x/tour/tree#Tree ] [here]] found.
// +build no-build OMIT
package main
import "golang.org/x/tour/tree"
// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
func Walk(t *tree.Tree, ch chan int)
// Same 检测树 t1 和 t2 是否含有相同的值。
func Same(t1, t2 *tree.Tree) bool
func main() {
}
- sync.Mutex
We have seen very suitable channel for communication between the various Go away.
But if we do not need to communicate it? For example, if we just want to ensure that only one process can access the Go a shared variable, so as to avoid conflict?
The concept involved here is called a mutex (Mutual Exclusion) *, we usually use mutex (Mutex) the data structures to provide such a mechanism.
Providing Go [[standard library https://go-zh.org/pkg/sync/#Mutex ] [ sync.Mutex
]] and the mutex type two methods:
Lock
Unlock
We can call the code before the Lock
method, after the code calls the Unlock
method to ensure the implementation of a code of mutual exclusion. See Inc
methods.
We can also use the defer
statement to ensure mutex will be unlocked. See Value
methods.
// +build OMIT
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
c.v[key]++
c.mux.Unlock()
}
// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
- Exercise: Web Crawler
In this exercise, we will use Go's concurrency features to parallelize a Web crawler.
Modify Crawl
function parallel grab URL, and guaranteed not repeated.
Tip : You can use a map to cache already acquired URL, but be careful map itself is not complicated by security!
// +build OMIT
package main
import (
"fmt"
)
type Fetcher interface {
// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
Fetch(url string) (body string, urls []string, err error)
}
// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher) {
// TODO: 并行的抓取 URL。
// TODO: 不重复抓取页面。
// 下面并没有实现上面两种情况:
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q\n", url, body)
for _, u := range urls {
Crawl(u, depth-1, fetcher)
}
return
}
func main() {
Crawl("https://golang.org/", 4, fetcher)
}
// fakeFetcher 是返回若干结果的 Fetcher。
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher 是填充后的 fakeFetcher。
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}
- Where next?
Go [[ https://go-zh.org/doc/ ] [document]] is an excellent start.
It contains reference guides, videos and much more information.
Learn how to organize Go code and working on it, see [[ https://www.youtube.com/watch?v=XCsL89YtqCs ] [This video]], or read [[ https://go-zh.org/ DOC / code.html ] [how to write Go Code]].
If you need help with the standard library, please refer to [[ https://go-zh.org/pkg/ ] [package manual]]. If it is the language itself helps, read [[ https://go-zh.org/ref/spec ] [language specification]] is a pleasant thing.
Go further explore the concurrency model, see [https://www.youtube.com/watch?v=f6kdp27TYZs][Go concurrency model] and [https://www.youtube.com/watch?v=QDDwwePbDtw] [depth Go concurrency model] and read [[ https://go-zh.org/doc/codewalk/sharemem/ ] [via shared memory communication]] code journey.
Want to start writing Web applications, see [https://vimeo.com/53221558] [a simple programming environment] and read [[ https://go-zh.org/doc/articles/wiki/ ] [write Web applications]] guide.
[[ Https://go-zh.org/doc/codewalk/functions/ ] [Function: Go first class citizen]] shows interesting function types.
[[ Https://blog.go-zh.org/ ] [Go blog]] has numerous articles and information on Go's.
[[ Https://www.mikespook.com/tag/golang/ ] [mikespook's blog]] There are a large number of Chinese and translated articles on Go's.
Open source e-book [[ https://github.com/astaxie/build-web-application-with-golang ] [Go Web programming]] and [[ https://github.com/Unknwon/the-way-to- go_ZH_CN ] [Go Getting Started Guide]] can help you learn more in-depth understanding and Go language.
Visit [[ https://go-zh.org ] [go-zh.org]] to learn more.