Merging other languages

This commit is contained in:
James Miranda
2016-09-23 18:01:10 -03:00
parent 380a8ee74c
commit de3c5bdaa4
490 changed files with 24539 additions and 24588 deletions

View File

@@ -1,240 +1,242 @@
# 2.7 并发
有人把Go比作21世纪的C语言第一是因为Go语言设计简单第二21世纪最重要的就是并行程序设计而Go从语言层面就支持了并行。
## goroutine
goroutine是Go并行设计的核心。goroutine说到底其实就是线程但是它比线程更小十几个goroutine可能体现在底层就是五六个线程Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB)当然会根据相应的数据伸缩。也正因为如此可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`go`关键字实现了,其实就是一个普通的函数。
go hello(a, b, c)
通过关键字go就启动了一个goroutine。我们来看一个例子
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") //开一个新的Goroutines执行
say("hello") //当前Goroutines执行
}
// 以上程序执行后将输出
// hello
// world
// hello
// world
// hello
// world
// hello
// world
// hello
我们可以看到go关键字很方便的就实现了并发编程。
上面的多个goroutine运行在同一个进程里面共享内存数据不过设计上我们要遵循不要通过共享来通信而要通过通信来共享。
> runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。
>默认情况下,调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显式调用 runtime.GOMAXPROCS(n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量并返回之前的设置。如果n < 1不会改变当前设置。以后Go的新版本中调度得到改进后这将被移除。这里有一篇Rob介绍的关于并发和并行的文章http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide
## channels
goroutine运行在相同的地址空间因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比可以通过它发送或者接收值。这些值只能是特定的类型channel类型。定义一个channel时也需要定义发送到channel的值的类型。注意必须使用make 创建channel
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
channel通过操作符`<-`来接收和发送数据
ch <- v // 发送v到channel ch.
v := <-ch // 从ch中接收数据并赋值给v
我们把这些应用到我们的例子中来:
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x + y)
}
默认情况下channel接收和发送数据都是阻塞的除非另一端已经准备好这样就使得Goroutines同步变的更加的简单而不需要显式的lock。所谓阻塞也就是如果读取value := <-ch它将会被阻塞直到有数据接收。其次任何发送ch<-5将会被阻塞直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。
## Buffered Channels
上面我们介绍了默认的非缓存类型的channel不过Go也允许指定channel的缓冲大小很简单就是channel可以存储多少元素。ch:= make(chan bool, 4)创建了可以存储4个元素的bool 型channel。在这个channel 中前4个元素可以无阻塞的写入。当写入第5个元素时代码将会阻塞直到其他goroutine从channel 中读取一些元素,腾出空间。
ch := make(chan type, value)
当 value = 0 时channel 是无缓冲阻塞读写的当value > 0 时channel 有缓冲、是非阻塞的,直到写满 value 个元素才阻塞写入。
我们看一下下面这个例子你可以在自己本机测试一下修改相应的value值
package main
import "fmt"
func main() {
c := make(chan int, 2)//修改2为1就报错修改2为3可以正常运行
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
//修改为1报如下的错误:
//fatal error: all goroutines are asleep - deadlock!
## RangeClose
上面这个例子中我们需要读取两次c这样不是很方便Go考虑到了这一点所以也可以通过range像操作slice或者map一样操作缓存类型的channel请看下面的例子
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 1, 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)
}
}
`for i := range c`能够不断的读取channel里面的数据直到该channel被显式的关闭。上面代码我们看到可以显式的关闭channel生产者通过内置函数`close`关闭channel。关闭channel之后就无法再发送任何数据了在消费方可以通过语法`v, ok := <-ch`测试channel是否被关闭。如果ok返回false那么说明channel已经没有任何数据并且已经被关闭。
>记住应该在生产者的地方关闭channel而不是消费的地方去关闭它这样容易引起panic
>另外记住一点的就是channel不像文件之类的不需要经常去关闭只有当你确实没有任何发送数据了或者你想显式的结束range循环之类的
## Select
我们上面介绍的都是只有一个channel的情况那么如果存在多个channel的时候我们该如何操作呢Go里面提供了一个关键字`select`,通过`select`可以监听channel上的数据流动。
`select`默认是阻塞的只有当监听的channel中有发送或接收可以进行时才会运行当多个channel都准备好的时候select是随机的选择一个执行的。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 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)
}
`select`里面还有default语法`select`其实就是类似switch的功能default就是当监听的channel都没有准备好的时候默认执行的select不再阻塞等待channel
select {
case i := <-c:
// use i
default:
// 当c阻塞的时候执行这里
}
## 超时
有时候会出现goroutine阻塞的情况那么我们如何避免整个程序进入阻塞的情况呢我们可以利用select来设置超时通过如下的方式实现
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
## runtime goroutine
runtime包中有几个处理goroutine的函数
- Goexit
退出当前执行的goroutine但是defer函数还会继续调用
- Gosched
让出当前goroutine的执行权限调度器安排其他等待的任务运行并在下次某个时候从该位置恢复执行。
- NumCPU
返回 CPU 核数量
- NumGoroutine
返回正在执行和排队的任务总数
- GOMAXPROCS
用来设置可以并行计算的CPU核数的最大值并返回之前的值。
## links
* [目录](<preface.md>)
* 上一章: [interface](<02.6.md>)
* 下一节: [总结](<02.8.md>)
# Concurrency
It is said that Go is the C language of the 21st century. I think there are two reasons: first, Go is a simple language; second, concurrency is a hot topic in today's world, and Go supports this feature at the language level.
## goroutine
goroutines and concurrency are built into the core design of Go. They're similar to threads but work differently. More than a dozen goroutines maybe only have 5 or 6 underlying threads. Go also gives you full support to sharing memory in your goroutines. One goroutine usually uses 4~5 KB of stack memory. Therefore, it's not hard to run thousands of goroutines on a single computer. A goroutine is more lightweight, more efficient and more convenient than system threads.
goroutines run on the thread manager at runtime in Go. We use the `go` keyword to create a new goroutine, which is a function at the underlying level ( ***main() is a goroutine*** ).
go hello(a, b, c)
Let's see an example.
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") // create a new goroutine
say("hello") // current goroutine
}
Output
hello
world
hello
world
hello
world
hello
world
hello
We see that it's very easy to use concurrency in Go by using the keyword `go`. In the above example, these two goroutines share some memory, but we would better off following the design recipe: Don't use shared data to communicate, use communication to share data.
runtime.Gosched() means let the CPU execute other goroutines, and come back at some point.
The scheduler only uses one thread to run all goroutines, which means it only implements concurrency. If you want to use more CPU cores in order to take advantage of parallel processing, you have to call runtime.GOMAXPROCS(n) to set the number of cores you want to use. If `n<1`, it changes nothing. This function may be removed in the future, see more details about parallel processing and concurrency in this [article](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
## channels
goroutines run in the same memory address space, so you have to maintain synchronization when you want to access shared memory. How do you communicate between different goroutines? Go uses a very good communication mechanism called `channel`. `channel` is like a two-way pipeline in Unix shells: use `channel` to send or receive data. The only data type that can be used in channels is the type `channel` and the keyword `chan`. Be aware that you have to use `make` to create a new `channel`.
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
channel uses the operator `<-` to send or receive data.
ch <- v // send v to channel ch.
v := <-ch // receive data from ch, and assign to v
Let's see more examples.
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x + y)
}
Sending and receiving data in channels blocks by default, so it's much easier to use synchronous goroutines. What I mean by block is that a goroutine will not continue when receiving data from an empty channel, i.e (`value := <-ch`), until other goroutines send data to this channel. On the other hand, the goroutine will not continue until the data it sends to a channel, i.e (`ch<-5`), is received.
## Buffered channels
I introduced non-buffered channels above. Go also has buffered channels that can store more than a single element. For example, `ch := make(chan bool, 4)`, here we create a channel that can store 4 boolean elements. So in this channel, we are able to send 4 elements into it without blocking, but the goroutine will be blocked when you try to send a fifth element and no goroutine receives it.
ch := make(chan type, n)
n == 0 ! non-bufferblock
n > 0 ! buffernon-block until n elements in the channel
You can try the following code on your computer and change some values.
package main
import "fmt"
func main() {
c := make(chan int, 2) // change 2 to 1 will have runtime error, but 3 is fine
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
## Range and Close
We can use range to operate on buffer channels as in slice and map.
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 1, 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)
}
}
`for i := range c` will not stop reading data from channel until the channel is closed. We use the keyword `close` to close the channel in above example. It's impossible to send or receive data on a closed channel; you can use `v, ok := <-ch` to test if a channel is closed. If `ok` returns false, it means the there is no data in that channel and it was closed.
Remember to always close channels in producers and not in consumers, or it's very easy to get into panic status.
Another thing you need to remember is that channels are not like files. You don't have to close them frequently unless you are sure the channel is completely useless, or you want to exit range loops.
## Select
In the above examples, we only use one channel, but how can we deal with more than one channel? Go has a keyword called `select` to listen to many channels.
`select` is blocking by default and it continues to execute only when one of channels has data to send or receive. If several channels are ready to use at the same time, select chooses which to execute randomly.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 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)
}
`select` has a `default` case as well, just like `switch`. When all the channels are not ready for use, it executes the default case (it doesn't wait for the channel anymore).
select {
case i := <-c:
// use i
default:
// executes here when c is blocked
}
## Timeout
Sometimes a goroutine becomes blocked. How can we avoid this to prevent the whole program from blocking? It's simple, we can set a timeout in the select.
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
## Runtime goroutine
The package `runtime` has some functions for dealing with goroutines.
- `runtime.Goexit()`
Exits the current goroutine, but defered functions will be executed as usual.
- `runtime.Gosched()`
Lets the scheduler execute other goroutines and comes back at some point.
- `runtime.NumCPU() int`
Returns the number of CPU cores
- `runtime.NumGoroutine() int`
Returns the number of goroutines
- `runtime.GOMAXPROCS(n int) int`
Sets how many CPU cores you want to use
## Links
- [Directory](preface.md)
- Previous section: [interface](02.6.md)
- Next section: [Summary](02.8.md)