第3课:Go语言并发机制

要理解GO的并发机制,需要了解Go协程(Goroutines)与通道(Channels) GO语言正是通过这两个东西来实现的高并发

Go协程(Goroutines)
一个Go协程就是一个和其它Go协程在同一地址空间里但却独立运行的函数
go f("hello", "world") // f starts running
g() // does not wait for f to return
就像是在shell里使用 & 标记启动一个命令
Go协程不是线程(很像线程,但比线程更轻量。)
多个协程可以在系统线程上做多路通信。
当一个Go协程阻塞时,所在的线程会阻塞,但其它Go协程不受影响

通道(Channels)
通道是类型化的值,能够被Go协程用来做同步或交互信息
timerChan := make(chan time.Time)
go func() {
    time.Sleep(deltaT)
    timerChan <- time.Now() // send time on timerChan
}()
// Do something else; when ready, receive.
// Receive will block until timerChan delivers.
// Value sent is other goroutine's completion time.
completedAt := <-timerChan
通道中有Select语句
这select语句很像switch,但它的判断条件是基于通信,而不是基于值的等量匹配。
select {
case v := <-ch1:
    fmt.Println("channel 1 sends", v)
case v := <-ch2:
    fmt.Println("channel 2 sends", v)
default: // optional
    fmt.Println("neither channel was ready")
}

一个并发的例子:
1.启动后台程序
使用闭包封装一个后台操作。
下面是从输入通道拷贝数据到输出通道。
go func() { // copy input to output
    for val := range input {
        output <- val
    }
}()
这个for range操作会一直执行到处理掉通道内最后一个值。
2.一个简单的负载均衡
数据类型:
type Work struct {
    x, y, z int
}
一个worker的任务:
func worker(in <-chan *Work, out chan<- *Work) {
   for w := range in {
      w.z = w.x * w.y
      Sleep(w.z)
      out <- w
   }
}
必须保证当一个worker阻塞时其他worker仍能运行
runner:
func Run() {
   in, out := make(chan *Work), make(chan *Work)
   for i := 0; i < NumWorkers; i++ {
       go worker(in, out)
   }
   go sendLotsOfWork(in)
   receiveLotsOfResults(out)
}
很简单的任务,但如果没有并发机制,你仍然很难这么简单的解决



这个负载均衡的例子具有很明显的并行和可扩展性。
Worker数可以非常巨大。
Go语言的这种并发特征能的开发一个安全的、好用的、可扩展的、并行的软件变得很容易。
当然,对并发的正确理解也很重要:
并发不是并行
并发帮助实现并行
并发使并行(扩展等)变得容易

打赏  如对你有帮助,请我喝杯咖啡吧!