Skip to content

Commit 975a3a3

Browse files
committed
Remove 02.7.md spaces
1 parent 3cb9ed7 commit 975a3a3

File tree

1 file changed

+122
-122
lines changed

1 file changed

+122
-122
lines changed

Diff for: zh/02.7.md

+122-122
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,40 @@ goroutine是Go并行设计的核心。goroutine说到底其实就是协程,但
99
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`go`关键字实现了,其实就是一个普通的函数。
1010
```Go
1111

12-
go hello(a, b, c)
12+
go hello(a, b, c)
1313
```
1414
通过关键字go就启动了一个goroutine。我们来看一个例子
1515
```Go
1616

17-
package main
17+
package main
1818

19-
import (
20-
"fmt"
21-
"runtime"
22-
)
19+
import (
20+
"fmt"
21+
"runtime"
22+
)
2323

24-
func say(s string) {
25-
for i := 0; i < 5; i++ {
26-
runtime.Gosched()
27-
fmt.Println(s)
28-
}
29-
}
30-
31-
func main() {
32-
go say("world") //开一个新的Goroutines执行
33-
say("hello") //当前Goroutines执行
24+
func say(s string) {
25+
for i := 0; i < 5; i++ {
26+
runtime.Gosched()
27+
fmt.Println(s)
3428
}
35-
36-
// 以上程序执行后将输出:
37-
// hello
38-
// world
39-
// hello
40-
// world
41-
// hello
42-
// world
43-
// hello
44-
// world
45-
// hello
29+
}
30+
31+
func main() {
32+
go say("world") //开一个新的Goroutines执行
33+
say("hello") //当前Goroutines执行
34+
}
35+
36+
// 以上程序执行后将输出:
37+
// hello
38+
// world
39+
// hello
40+
// world
41+
// hello
42+
// world
43+
// hello
44+
// world
45+
// hello
4646
```
4747
我们可以看到go关键字很方便的就实现了并发编程。
4848
上面的多个goroutine运行在同一个进程里面,共享内存数据,不过设计上我们要遵循:不要通过共享来通信,而要通过通信来共享。
@@ -57,95 +57,95 @@ goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`g
5757
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:
5858
```Go
5959

60-
ci := make(chan int)
61-
cs := make(chan string)
62-
cf := make(chan interface{})
60+
ci := make(chan int)
61+
cs := make(chan string)
62+
cf := make(chan interface{})
6363
```
6464
channel通过操作符`<-`来接收和发送数据
6565
```Go
6666

67-
ch <- v // 发送v到channel ch.
68-
v := <-ch // 从ch中接收数据,并赋值给v
67+
ch <- v // 发送v到channel ch.
68+
v := <-ch // 从ch中接收数据,并赋值给v
6969
```
7070
我们把这些应用到我们的例子中来:
7171
```Go
7272

73-
package main
73+
package main
7474

75-
import "fmt"
75+
import "fmt"
7676

77-
func sum(a []int, c chan int) {
78-
total := 0
79-
for _, v := range a {
80-
total += v
81-
}
82-
c <- total // send total to c
77+
func sum(a []int, c chan int) {
78+
total := 0
79+
for _, v := range a {
80+
total += v
8381
}
82+
c <- total // send total to c
83+
}
8484

85-
func main() {
86-
a := []int{7, 2, 8, -9, 4, 0}
85+
func main() {
86+
a := []int{7, 2, 8, -9, 4, 0}
8787

88-
c := make(chan int)
89-
go sum(a[:len(a)/2], c)
90-
go sum(a[len(a)/2:], c)
91-
x, y := <-c, <-c // receive from c
88+
c := make(chan int)
89+
go sum(a[:len(a)/2], c)
90+
go sum(a[len(a)/2:], c)
91+
x, y := <-c, <-c // receive from c
9292

93-
fmt.Println(x, y, x + y)
94-
}
93+
fmt.Println(x, y, x + y)
94+
}
9595
```
9696
默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得Goroutines同步变的更加的简单,而不需要显式的lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。
9797

9898
## Buffered Channels
9999
上面我们介绍了默认的非缓存类型的channel,不过Go也允许指定channel的缓冲大小,很简单,就是channel可以存储多少元素。ch:= make(chan bool, 4),创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。
100100
```Go
101101

102-
ch := make(chan type, value)
102+
ch := make(chan type, value)
103103
```
104104
当 value = 0 时,channel 是无缓冲阻塞读写的,当value > 0 时,channel 有缓冲、是非阻塞的,直到写满 value 个元素才阻塞写入。
105105

106106
我们看一下下面这个例子,你可以在自己本机测试一下,修改相应的value值
107107
```Go
108108

109-
package main
109+
package main
110110

111-
import "fmt"
111+
import "fmt"
112112

113-
func main() {
114-
c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行
115-
c <- 1
116-
c <- 2
117-
fmt.Println(<-c)
118-
fmt.Println(<-c)
119-
}
113+
func main() {
114+
c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行
115+
c <- 1
116+
c <- 2
117+
fmt.Println(<-c)
118+
fmt.Println(<-c)
119+
}
120120
//修改为1报如下的错误:
121121
//fatal error: all goroutines are asleep - deadlock!
122122
```
123123
## Range和Close
124124
上面这个例子中,我们需要读取两次c,这样不是很方便,Go考虑到了这一点,所以也可以通过range,像操作slice或者map一样操作缓存类型的channel,请看下面的例子
125125
```Go
126126

127-
package main
127+
package main
128128

129-
import (
130-
"fmt"
131-
)
129+
import (
130+
"fmt"
131+
)
132132

133-
func fibonacci(n int, c chan int) {
134-
x, y := 1, 1
135-
for i := 0; i < n; i++ {
136-
c <- x
137-
x, y = y, x + y
138-
}
139-
close(c)
133+
func fibonacci(n int, c chan int) {
134+
x, y := 1, 1
135+
for i := 0; i < n; i++ {
136+
c <- x
137+
x, y = y, x + y
140138
}
141-
142-
func main() {
143-
c := make(chan int, 10)
144-
go fibonacci(cap(c), c)
145-
for i := range c {
146-
fmt.Println(i)
147-
}
139+
close(c)
140+
}
141+
142+
func main() {
143+
c := make(chan int, 10)
144+
go fibonacci(cap(c), c)
145+
for i := range c {
146+
fmt.Println(i)
148147
}
148+
}
149149
```
150150
`for i := range c`能够不断的读取channel里面的数据,直到该channel被显式的关闭。上面代码我们看到可以显式的关闭channel,生产者通过内置函数`close`关闭channel。关闭channel之后就无法再发送任何数据了,在消费方可以通过语法`v, ok := <-ch`测试channel是否被关闭。如果ok返回false,那么说明channel已经没有任何数据并且已经被关闭。
151151

@@ -159,66 +159,66 @@ channel通过操作符`<-`来接收和发送数据
159159
`select`默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。
160160
```Go
161161

162-
package main
162+
package main
163163

164-
import "fmt"
164+
import "fmt"
165165

166-
func fibonacci(c, quit chan int) {
167-
x, y := 1, 1
168-
for {
169-
select {
170-
case c <- x:
171-
x, y = y, x + y
172-
case <-quit:
173-
fmt.Println("quit")
174-
return
175-
}
166+
func fibonacci(c, quit chan int) {
167+
x, y := 1, 1
168+
for {
169+
select {
170+
case c <- x:
171+
x, y = y, x + y
172+
case <-quit:
173+
fmt.Println("quit")
174+
return
176175
}
177176
}
178-
179-
func main() {
180-
c := make(chan int)
181-
quit := make(chan int)
182-
go func() {
183-
for i := 0; i < 10; i++ {
184-
fmt.Println(<-c)
185-
}
186-
quit <- 0
187-
}()
188-
fibonacci(c, quit)
189-
}
177+
}
178+
179+
func main() {
180+
c := make(chan int)
181+
quit := make(chan int)
182+
go func() {
183+
for i := 0; i < 10; i++ {
184+
fmt.Println(<-c)
185+
}
186+
quit <- 0
187+
}()
188+
fibonacci(c, quit)
189+
}
190190
```
191191
`select`里面还有default语法,`select`其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
192192
```Go
193193

194-
select {
195-
case i := <-c:
196-
// use i
197-
default:
198-
// 当c阻塞的时候执行这里
199-
}
194+
select {
195+
case i := <-c:
196+
// use i
197+
default:
198+
// 当c阻塞的时候执行这里
199+
}
200200
```
201201
## 超时
202202
有时候会出现goroutine阻塞的情况,那么我们如何避免整个程序进入阻塞的情况呢?我们可以利用select来设置超时,通过如下的方式实现:
203203
```Go
204204

205-
func main() {
206-
c := make(chan int)
207-
o := make(chan bool)
208-
go func() {
209-
for {
210-
select {
211-
case v := <- c:
212-
println(v)
213-
case <- time.After(5 * time.Second):
214-
println("timeout")
215-
o <- true
216-
break
217-
}
205+
func main() {
206+
c := make(chan int)
207+
o := make(chan bool)
208+
go func() {
209+
for {
210+
select {
211+
case v := <- c:
212+
println(v)
213+
case <- time.After(5 * time.Second):
214+
println("timeout")
215+
o <- true
216+
break
218217
}
219-
}()
220-
<- o
221-
}
218+
}
219+
}()
220+
<- o
221+
}
222222
```
223223

224224
## runtime goroutine

0 commit comments

Comments
 (0)