理解go语言编程-并发编程
在工程上,有两种最常见的并发通信模型:共享数据和消息。共享数据是指多个并发单元分别保持对同一个数据的引用,实现对该数据的共享。被共享的数据可能有多种形式,比如内存数据块、磁盘文件、网络数据等。在实际工程应用中最常见的无疑是内存了,也就是常说的共享内存。Go语言提供的是另一种通信模型,即以消息机制而非共享内存作为通信方式。
消息机制认为每个并发单元是自包含的、独立的个体,并且都有自己的变量,但在不同并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。这有点类似于进程的概念,每个进程不会被其他进程打扰,它只做好自己的工作就可以了。不同进程间靠消息来通信,它们不会共享内存。
channel是Go语言在语言级别提供的goroutine间的通信方式。如果对Unix管道有所了解的话,就不难理解channel,可以将其认为是一种类型安全的管道。
我们通过ch <- 1语句向对应的channel中写入一个数据。在这个channel被读取前,这个操作是阻塞的。在所有的goroutine启动完成后,我们通过<-ch语句从10个channel中依次读取数据。在对应的channel写入数据前,这个操作也是阻塞的。这样,我们就用channel实现了类似锁的功能,进而保证了所有goroutine完成后主函数才返回。
1 | package main |
声明定义channel
1 | var m map[string] chan bool // 声明一个map,元素是bool型的channel |
Go语言直接在语言级别支持select关键字,用于处理异步IO问题。
select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。与switch语句可以选择任何可使用相等比较的条件相比,select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作.
select不像switch,后面并不带判断条件,而是直接去查看case语句
1 | select { |
创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞。
1 | c := make(chan int, 1024) |
select的特点是只要其中一个case已经完成,程序就会继续往下执行,而不会考虑其他case的情况,很方便地解决超时问题。
1 | // 首先,我们实现并执行一个匿名的超时等待函数 |
在Go语言中channel本身也是一个原生类型,与map之类的类型地位一样,因此channel本身在定义后也可以通过channel来传递。
1 | type PipeData struct { |
单向channel,非const指针具备const指针的所有功能,将一个指针设定为const就是明确告诉函数实现者不要试图对该指针进行修改。单向channel也是起到这样的一种契约作用
1 | var ch1 chan int // ch1是一个正常的channel,不是单向的 |
关闭channel,close(ch),x, ok := <-ch 这个用法与map中的按键获取value的过程比较类似,只需要看第二个bool返回值即可,如果返回值是false则表示ch已经被关闭。
runtime.GOMAXPROCS(16)设置多核,runtime.NumCPU()查看当前核数。Gosched()可以出让时间片。
Go语言包中的sync包提供了两种锁类型:sync.Mutex和sync.RWMutex。从RWMutex的实现看,RWMutex类型其实组合了Mutex
1 | type RWMutex struct { |
go语言提供了一个Once类型来保证全局的唯一性操作,sync包中还包含一个atomic子包,func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool)
1 | var a string |