并发的特点是需要锁Lock和互斥Mutex。在Java中加锁和解锁是一个复杂过程代码如下:
try {
mutex.acquire();
try {
// do something
} finally {
mutex.release();
}
} catch(InterruptedException ie) {
// ...
}
并发另外一个特性是异步,各种语言都有自己的异步机制,基于回调的有:
但是不能很好地与并行共处,依赖各种库包,代码难于调试,易陷入回调嵌套地狱。见callbackhell.com
Go的并发基于两个概念:
比如sleep和taalk代码如下:
func sleepAndTalk(t time.Duration, msg string) {
time.Sleep(t)
fmt.Printf("%v ", msg)
}
每秒一个消息:
func main() {
sleepAndTalk(0*time.Second, "Hello")
sleepAndTalk(1*time.Second, "Gophers!")
sleepAndTalk(2*time.Second, "What's")
sleepAndTalk(3*time.Second, "up?")
}
如果我们不是每秒,而是需要同时发送消息呢?加上go即可:
func main() {
go sleepAndTalk(0*time.Second, "Hello")
go sleepAndTalk(1*time.Second, "Gophers!")
go sleepAndTalk(2*time.Second, "What's")
go sleepAndTalk(3*time.Second, "up?")
}
这是main开启一个主协程,当其结束整个程序也就结束。
下面我们看看通过Channel进行通讯,这时sleepAndTalk 就不是打印出信息,而是将字符串发送给channel了。
func sleepAndTalk(secs time.Duration, msg string, c chan string) {
time.Sleep(secs * time.Second)
c <- msg
}
我们创建channel然后将其传递给sleepAndTalk, 之后就可以等待数据值发送到channel了:
func main() {
c := make(chan string)
go sleepAndTalk(0, "Hello", c)
go sleepAndTalk(1, "Gophers!", c)
go sleepAndTalk(2, "What's", c)
go sleepAndTalk(3, "up?", c)
for i := 0; i < 4; i++ {
fmt.Printf("%v ", <-c)
}
}
下面看看如何在Web环境中实现:首先我们从Channel中接受到nextId:
var nextID = make(chan int)
func handler(w http.ResponseWriter, q *http.Request) {
fmt.Fprintf(w, "<h1>You got %v<h1>", <-nextID)
}
需要一个协程发送nextID到channel中。
func main() {
http.HandleFunc("/next", handler)
go func() {
for i := 0; ; i++ {
nextID <- i
}
}()
http.ListenAndServe("localhost:8080", nil)
}
通过浏览器访问localhost:8080/next 可得到nextID数值。
如果有多个Channel,那么代码如下:
var battle = make(chan string)
func handler(w http.ResponseWriter, q *http.Request) {
select {
case battle <- q.FormValue("usr"):
fmt.Fprintf(w, "You won!")
case won := <-battle:
fmt.Fprintf(w, "You lost, %v is better than you", won)
}
}
这样访问的URL参数不同:
Go - localhost:8080/fight?usr=go Java - localhost:8080/fight?usr=java
多个Channel可以串联组成流:
gophers链:
func f(left, right chan int) {
left <- 1 + <-right
}
func main() {
start := time.Now()
const n = 1000
leftmost := make(chan int)
right := leftmost
left := leftmost
for i := 0; i < n; i++ {
right = make(chan int)
go f(left, right)
left = right
}
go func(c chan int) { c <- 0 }(right)
fmt.Println(<-leftmost, time.Since(start))
}