Golang :不要通过共享内存来通信,而应该通过通信来共享内存。这句风靡在Go社区的话,说的就是 goroutine中的 channel …….
他在go并发编程中充当着 类型安全的管道作用。
1.通过golang中的 goroutine 与sync.Mutex进行 并发同步
import(
"fmt"
"sync"
"runtime"
)
var count int =0;
func counter(lock * sync.Mutex){
lock.Lock()
count++
fmt.Println(count)
lock.Unlock()
}
func main(){
lock:=&sync.Mutex{}
for i:=0;i<10;i++{
//传递指针是为了防止 函数内的锁和 调用锁不一致
go counter(lock)
}
for{
lock.Lock()
c:=count
lock.Unlock()
///把时间片给别的goroutine 未来某个时刻运行该routine
runtime.Gosched()
if c>=10{
fmt.Println("goroutine end")
break
}
}
}
2、goroutine之间通过 channel进行通信,channel是和类型相关的 可以理解为 是一种类型安全的管道。
简单的channel 使用
package main
import "fmt"
func Count(ch chan int) {
ch <- 1
fmt.Println("Counting")
}
func main() {
chs := make([]chan int, 10)
for i := 0; i < 10; i++ {
chs[i] = make(chan int)
go Count(chs[i])
fmt.Println("Count",i)
}
for i, ch := range chs {
<-ch
fmt.Println("Counting",i)
}
}
3、Go语言中的select是语言级内置 非堵塞
select {
case <-chan1: // 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句
default: // 如果上面都没有成功,则进入default处理流程
}
可以看出,select不像switch,后面并不带判断条件,而是直接去查看case语句。每个case语句都必须是一个面向channel的操作。比如上面的例子中,第一个case试图从chan1读取一个数据并直接忽略读到的数据,而第二个case则是试图向chan2中写入一个整型数1,如果这两者都没有成功,则到达default语句。
4、channel 的带缓冲读取写入
之前我们示范创建的都是不带缓冲的channel,这种做法对于传递单个数据的场景可以接受,但对于需要持续传输大量数据的场景就有些不合适了。接下来我们介绍如何给channel带上缓冲,从而达到消息队列的效果。
要创建一个带缓冲的channel,其实也非常容易:
c := make(chan int, 1024)
在调用make()时将缓冲区大小作为第二个参数传入即可,比如上面这个例子就创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞。
从带缓冲的channel中读取数据可以使用与常规非缓冲channel完全一致的方法,但我们也可以使用range关键来实现更为简便的循环读取:
for i := range c {
fmt.Println("Received:", i)
}
5、用goroutine模拟生产消费者
package main
import "fmt"
import "time"
func Producer (queue chan<- int){
for i:= 0; i < 10; i++ {
queue <- i
}
}