goroutine之间的通信使用channel。数据传送是阻塞式的,发了数据之后必须有人来收数据。
func chanDemo() {
//var c chan int // c == nil
c := make(chan int)
go func() { //这里的匿名函数相当于闭包,引用了外面的c变量
for {
n := <-c //开了一个goroutine去接数据
fmt.Println(n)
}
}()
c <- 1 //往channel中发生数据
c <- 2
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
channel作为参数
func woker(id int, c chan int) {
for {
fmt.Printf("Worker %d received %c\n", id, <-c) //因为协程是主动式非抢占,在遇到I/O操作时,会进行调度
}
}
func chanDemo() {
var channels [10]chan int
for i := 0; i < 10; i++ {
channels[i] = make(chan int)
go woker(i, channels[i])
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond) //防止main函数先退出
}
func main() {
chanDemo()
}
channel作为返回值
func createWorker(id int) chan<- int { //返回一个只能往里写数据的channel
c := make(chan int) //创建一个可以写数据和读数据的channnel
go func() {
for {
fmt.Printf("Worker %d received %c\n", id, <-c)
}
}()
return c
}
func chanDemo() {
var channels [10]chan<- int //只能往channel中写数据
for i := 0; i < 10; i++ {
//channels[i] = make(chan int)
//go woker(i, channels[i])
channels[i] = createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
bufferchannel
func woker(id int, c chan int) {
for {
fmt.Printf("Worker %d received %c\n", id, <-c) //因为协程是主动式非抢占,在遇到I/O操作时,会进行调度
}
}
func bufferedChannel() {
c := make(chan int, 3) //创建缓冲区为3的channel
go woker(0, c)
//只要有人发数据,就必须有人来接数据。缓冲区为3说明 发送的数据小于3时,若没人来接数据,不会出现错误
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Millisecond)
}
channel是可以被发送方进行关闭的,接收方使用两种方法进行判断。
func woker(id int, c chan int) {
for {
//当发送方关闭channel之后,接收方还会进行接收,值为channel对应类型的默认值。因此在这里进行判断是否channel关闭
if n, ok := <-c; ok {
fmt.Printf("Worker %d received %c\n", id, n) //因为协程是主动式非抢占,在遇到I/O操作时,会进行调度
} else {
break
}
}
//第二种方法
//for n := range c {
// fmt.Printf("Worker %d received %c\n", id, n)
//}
}
func channelClosed() {
c := make(chan int, 3) //创建缓冲区为3的channel
go woker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
close(c)
time.Sleep(time.Millisecond)
}
不要通过共享内存来通信,通过通信来共享内存。
使用channel等待任务结束。两种方法:使用channel进行传递信息;使用sync.WaitGroup类型的变量。
data := make(chan int)
exit := make(chan bool)
go func() {
for d := range data {
fmt.Println(d)
}
fmt.Println("recv over.")
exit <- true
}()
data <- 1
data <- 2
data <- 3
close(data)
fmt.Println("send over.")
<-exit
func doWoker(id int, c chan int, done chan bool) {
for n := range c {
fmt.Printf("Worker %d received %c\n", id, n)
//done <- true //往done中发送了数据,在外面必须有人来接收数据
go func() {
done <- true
}()
}
}
type worker struct {
in chan int
done chan bool
}
func createWorker(id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
go doWoker(id, w.in, w.done)
return w
}
func chanDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i)
}
for i, worker := range workers {
worker.in <- 'a' + i
//<-workers[i].done //只有收到了数据说明打印已经完成了,才会往下执行
}
for i, worker := range workers {
worker.in <- 'A' + i
//<-workers[i].done
}
//将20个数据全部发出去,然后再进行等待
for _, worker := range workers {
<-worker.done
<-worker.done
}
}
func doWoker(id int, w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c\n", id, n)
w.done()
}
}
type worker struct {
in chan int
done func()
}
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
done: func() {
wg.Done() //完成一次任务调用一次Done()
},
}
go doWoker(id, w)
return w
}
func chanDemo() {
var workers [10]worker
var wq sync.WaitGroup
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wq)
}
wq.Add(20) //添加20个任务
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
wq.Wait()
}
使用select进行调度。
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(
time.Duration(rand.Intn(1500)) *
time.Millisecond)
out <- i
i++
}
}()
return out
}
func woker(id int, c chan int) {
for n := range c {
time.Sleep(time.Second)
fmt.Printf("Worker %d received %d\n", id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go woker(id, c)
return c
}
func main() {
//从c1, c2中读出数据 写入worker中
var c1, c2 = generator(), generator()
worker := createWorker(0)
var values []int //生成和消耗的速度不一样,需要存储接收到的数据
tm := time.After(10 * time.Second) //返回的是一个channel,10s后往这个channel中发生一个时间
tick := time.Tick(time.Second)
for {
var activeWorker chan<- int // activeWorker 是 nil,在select虽然不会运行错误,但是永远不是被select到
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
case <-time.After(800 * time.Millisecond): //如果两次生成数据的时间大于800ms,则会timeout
fmt.Println("Time out")
case <-tick: //每1s查看一下数据的长度
fmt.Println("queue lens = ", len(values))
case <-tm:
fmt.Println("Bye")
return
}
}
}
传统的同步机制:WaitGroup,mutex,conditional variable
type atomicInt struct {
value int
m sync.Mutex
}
func (a *atomicInt) increasement() {
fmt.Println("safe increasement")
func() {
a.m.Lock()
defer a.m.Unlock()
a.value++
}()
}
func (a *atomicInt) get() int {
a.m.Lock()
defer a.m.Unlock()
return a.value
}
通过select可以监听channel上的数据流动。
每一个case语句里必须是一个I/O操作。
elect {
case <-chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}
在一个select语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语句。
如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。
如果没有default语句,那么select语句将被阻塞,直到至少有一个通信可以进行下去。
go语言支持两种形式的并发:多线程共享内存;CSP并发模型。
多线程共享内存模型:在访问数据的时候,通过锁来访问。
CSP并发模型:通过channel和goroutine实现。goroutine是Go中并发的执行单位,channel是各个并发单位之前的通信机制,通俗来说就是各个goroutine之间的管道。
传数据用channel <- data
,取数据用<-channel
,在通信过程中,传数据channel <- data
和取数据<-channel
必然会成对出现,而且不管传还是取,必阻塞,直到另外的goroutine
传或者取为止。
参考一
参考二