channel简单示例

package main

import (
"fmt"
"time"
) //channel的创建,发送,接收
func channe1(){
//创建,channel是有类型的
c1 := make(chan int) //接收,在这段程序中接收方必须是一个goroutine,因为只在主程序中发送而不接收,程序会报deadlock
//通常使用匿名函数开一个与主程序同时执行的内部方法,即并行执行
go func(){
fmt.Println("接收数据准备")
//这里接收channel使用了io输出功能,io是可以被抢占控制权的,即IO的特性
fmt.Println(<- c1)
fmt.Println("接收数据完成") //关闭,不显式关闭时,channel会随主程序(即main)的运行结束而结束
//如果“接收”处理数据的时间较长,就会出现主程序已经结束,但接收方还没处理完的情况
//此时可以让主程序sleep一段时间,等待接收方把数据处理完毕再关闭
close(c1) fmt.Println("接收结束")
}() //发送数据,“接收”程序要在发送之前准备,
//意思就是发送数据之前,要先为channel准备好接收;
//否则,执行<- 1将1发送到channel时,go发现没有人接收,会报deadlock
c1 <- //接收方与主程序同时执行
//主程序在此停止1毫秒,就相当于主程序等了接收方一毫秒
time.Sleep(time.Millisecond)
} func main(){
channe1()
fmt.Println("主程序结束")
}

输出

# go run chan1.go
接收数据准备 接收数据完成
接收结束
主程序结束

channel同步

如果一个动作会触发另外一个动作,那么这个行为通常被称为事件(event);如果这个事件不附带信息,那么此类事件又通常被用于同步。

channel有发送、接收、关闭三个操作;

发送触发接收,如果一个channel不发送,那么接收将处于阻塞。这种同步,可用于消息通知。

package main

import (
"fmt"
"time"
) func test(){
c := make(chan struct{}) go func(){
fmt.Println("我要花两分钟去看看园子里的花还活着吗")
time.Sleep(*time.Second)
c <- struct{}{}
}() //程序会在这里等待7秒
<- c
//然后打印出下面这句话
fmt.Println("这花从屋里移值出去后,活得比以前更好了")
} func main(){
test()
}

channel数组

package main

import (
"fmt"
"sync"
) func channel2(){
//WaitGroup的wait(在主程序中调用)与done(在与主程序并行执行的“接收”方中调用)的交互,
//可以达到等待所有channel运行完毕,再让主程序运行的效果
//而不是程序员猜想channel“接收”需要多少时间运行,
//然后去主程序中设置time.Sleep让主程序等待
var wtg sync.WaitGroup //channel数组
var workers []worker
for i := ; i < ; i++{
//使用引用的方式传送参数,所有的channel公用一个WaitGroup
workers[i] = createWorker(i,&wtg)
}
//要一次性添加完要等待执行的channel个数
wtg.Add()
for i,worker := range workers {
worker.in <- 'a' + i
//wtg.Add(1) //这种方式会报错
} for i,worker := range workers {
worker.in <- 'A' + i
//wtg.Add(1)
}
//等待所有channel执行完毕,否则一直阻塞
wtg.Wait()
fmt.Println("所有channel执行完毕")
} func createWorker(id int, wtg *sync.WaitGroup) worker{
//channel作为struct的一个属性
wk := worker{
//chan<-表示channel只用于发送数据,即输入
in : make(chan int),
done: func(){
wtg.Done()
},
}
//channel创建之后,就开始以并行的方式建立“接收”方
go wk.doWork(id)
return wk
} type worker struct {
in chan int
done func()
} //“接收”方程序
func (wk *worker) doWork(id int){ //接收的确是按数组顺序顺序打印出来的,但这只是程序第一次运行的情况
//接收是在发送之前就以并行的方式运行起来了,之后数据中每个channel都一直处于阻塞等待状态
//也就是说数组中的每个channel谁先打印出数据,就表示该谁先发送数据(忽略channel传送数据时长差异)
for n := range wk.in {
fmt.Printf("第 %d 次接收的信息为 %c\n",id,n) //通知主程序工作处理完毕
wk.done()
}
} func main(){
channel2()
fmt.Println("主程序结束")
}

输出

# go run chann.go
第 次接收的信息为 a
第 次接收的信息为 b
第 次接收的信息为 c
第 次接收的信息为 d
第 次接收的信息为 e
第 次接收的信息为 f
第 次接收的信息为 g
第 次接收的信息为 G
第 次接收的信息为 h
第 次接收的信息为 H
第 次接收的信息为 A
第 次接收的信息为 B
第 次接收的信息为 C
第 次接收的信息为 D
第 次接收的信息为 E
第 次接收的信息为 F
所有channel执行完毕
主程序结束

这个例子除了介绍buffer channel,即channel数组的使用外,还涉及一个GO语言的重要思想,引用GO创始人的原话:

Don't communicate by sharing memory;share memory by communicating.

不要通过共享内存来通信;通过通信来共享内存。

通过共享内存来通信:程序A运行完的结果返回给标识flag,如果flag为true运行程序B,返之运行程序C;

通过通信来共享内存:当一个channel处理完毕时,不是修改一个变量告诉发送方我处理完了,而通过channel来传达这个信息; 可以再定义一个channel来实现这个功能,此处使用了WaitGroup。

select调度

最简单的select调度

package main

import (
"fmt"
) func main(){
var c1,c2 chan int //nil //select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n)
//如果所有channel都无数据
default:
fmt.Println("无数据可接收")
} }

输出

# go run chan3.go
无数据可接收

select调度是多选一,是阻塞的

package main

import (
"fmt"
) func channel3() chan int{
out := make(chan int)
go func(){
i :=
for {
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3() for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n)
/* 这里注释掉了,为什么呢?
这是因为如果不注释掉,大部分程序就会走default
因为在一个时间段内,发送数据只是占用了部分时间片,而不是所有时间
该行注释掉之后,select只有两个选择,输出c1与c2发送过来的数据 //如果所有channel都无数据
default:
fmt.Println("无数据可接收")
*/
}
} }

输出,只取一部分输出数据

来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:
来自c1:

如果select中的default不注释

package main

import (
"fmt"
) func channel3() chan int{
out := make(chan int)
go func(){
i :=
for {
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3()
i :=
j :=
for{
i++
fmt.Println("循环:",i)
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) default:
j++
fmt.Println("无数据可接收",j)
}
} }

输出,12755次循环中有12635次走了无数据

循环:
无数据可接收
循环:
无数据可接收
循环:
无数据可接收

无default

package main

import (
"fmt"
) func channel3() chan int{
out := make(chan int)
go func(){
i :=
for {
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3()
i :=
j :=
t :=
for{
i++
fmt.Println("循环:",i)
t = i - j
fmt.Println("来自c1:",t)
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
j++
fmt.Println("来自c2:",n)
}
}
}

输出

循环:
来自c1:
来自c2:
循环:
来自c1:
来自c2:
循环:
来自c1:
来自c2:
循环:
来自c1:
来自c2:
package main
import (
"fmt"
"time"
) func ch1() chan int{
fmt.Println("接收程序开始") c1 := make(chan int) go func(){
i:=
for {
c1 <- i
}
}() time.Sleep(time.Millisecond)
return c1
} func main(){
a,b,i := ,,
c1,c2 := ch1(),ch1() for{
i++ select{
case <- c1:
a++
case <- c2:
b++
}
d := a+b
fmt.Println("all:",i,d,a,b)
} fmt.Println("主程序结束")
}
all:
all:
all:
all:
all:
all:
all:
all:
all:
all:
all:
all:
all:
all:

有default时,select几乎99%都选择了default,这说明每一次循环(每一个时间片)中,有数据的情况只是占这个时间片的一小部分,是极小的一部分,无数据的情况占绝大部分(12635/12755);

无default时,select在多个channel中的选择机会基本均等(36918/37428),并且,总的循环次数=所有case条件执行次数之和,

这意味着没有任何一次for循环是轮空的,尽管case有数据的情况只是占用一次循环时间中极小的一部分;

意味着每一次的for循环,即每一次的select执行,必会等待一个case满足条件,本次select才会结束;

意味着select是阻塞的,即至少有一个case满足条件时,select才会结束,否则就等待

再次验证一下

package main
import (
"fmt"
"time"
) func ch1() chan int{
fmt.Println("接收程序开始") c1 := make(chan int) go func(){
i:=
for {
c1 <- i
time.Sleep(time.Millisecond*)
}
}() return c1
} func main(){
a,b,i := ,,
c1,c2 := ch1(),ch1() for{
i++ select{
case <- c1:
a++
case <- c2:
b++
}
d := a+b
fmt.Println("all:",i,d,a,b)
} fmt.Println("主程序结束")
}

每发送一次数据行,等待3秒;select每次等待3秒输出

接收程序开始
接收程序开始
all:
all:
all:
all:
all:
all:
all:
all:

如果两个channel同时到达select,那么select二选一,即随机选择一个优先输出

如果不同时到达,则谁先达就选择谁,代码如下

package main

import (
"fmt"
"math/rand"
"time"
) func channel3() chan int{
out := make(chan int)
go func(){
i :=
for {
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3() for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) }
} }

输出

来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c1:
来自c2:
来自c2:
来自c2:
来自c1:
来自c1:

前面有说,一个channel必须先定义接收处理的程序,才能开始发送数据,这而代码却是先发送数据,之后才是select接收,这不是与之前所说的相矛盾吗?

那来验证一下,使用最简单的方式先发送再让select接收试一试

package main

import (
"fmt"
//"math/rand"
//"time"
)
/*
func channel3() chan int{
out := make(chan int)
go func(){
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1000))*
time.Millisecond)
out <- i
i++
}
}()
return out
}
*/ func main(){
//var c1,c2 = channel3(),channel3()
var c1,c2 = make(chan int),make(chan int)
c1 <-
c2 <- for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) }
}
}

输出错误deadlock

fatal error: all goroutines are asleep - deadlock!

goroutine  [chan send]:
main.main()
/usr/local/automng/src/goapp/src/test/channel3/chan3.go: +0x92
exit status

那为什么开始的代码没有报错?

channel是goroutine与goroutine之间的通信

这是因为发送方位于一个goroutine中(go func(){...}())中,goroutine处理了这个问题,

前面有程序中只有一个go,也就是只有一个goroutine,为什么也能运行?

这是因为go程序中,main方法本身也是一个goroutine

下面的代码就是正确的

package main

import (
"fmt"
//"math/rand"
//"time"
)
/*
func channel3() chan int{
out := make(chan int)
go func(){
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1000))*
time.Millisecond)
out <- i
i++
}
}()
return out
}
*/ func main(){
//var c1,c2 = channel3(),channel3()
var c1,c2 = make(chan int),make(chan int) go func(){
for {
c1 <-
c2 <-
//time.Sleep(time.Millisecond)
}
}() for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) }
}
}

输出,不断地发送1和2,然后又不断地输出1和2

来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:
来自c2:
来自c1:

这里的输出与之前的输出又不一样,之前的输出一片c1或c2,而这里是c1与c2交替式输出,一个c1下来接着就是c2,这样的区别很明显;下面的这段话是个人推测,不保证百分之百是这样哈

首先,在计算机中我们看到一个功能正在运行,比如复制一个电影,但实际上“一个”CPU还在同时做很多工作,比如你还打开了一个网页在看视频,只是这个CPU处理任务的速度非常快,至少在我们看来,这两个工作都在进行着;但对计算机来说,这些工作是在不同的时间片中完成的,同时IO流也在不同的设备上快速切换着。

GO程序是非抢占式的,就是GO程序拿到CPU资源之后,自己不释放别人不可以抢走;GO中也有可以抢占式的程序,即别人可以抢走程序执行的控制权,比如IO操作(fmt打印就是输出IO到屏幕),这种操作会中断GO对资源的占用,我们可以认为这是系统需要这样,而GO是运行在系统上才不得不这样;比如一个GO主程序正在执行代码,到fmt的时候,在一些时间片上系统抢了GO程序的资源干了别的事,然后又把资源还给了GO程序。

channel操作也是非抢占式的,先给哪个channel发送数据,那么这个channel如果不被中断,那么它就会先输出,先输出就会被select调度先输出;for中先c1,然后 fmt中断一次,再c2再fmt中断一次,然后下一轮for循环,所以保持了c1,c2,c1,c2……的输出顺序

而成片输出c1或c2的代码中,channel的创建是在方法中,数据发送则是在两个不同的goroutine中,这两个goroutine是并行执行,而不像for里面c1与c2,

for {
c1 <-
c2 <-
//time.Sleep(time.Millisecond)
}

程序必定是先c1再c2这么有顺序,下面将变量i变成全局变量,再次执行之前的代码

文章有些长了,再次重贴一下之前的代码,重运行看效果

package main

import (
"fmt"
//"math/rand"
//"time"
) var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//time.Sleep(time.Duration(rand.Intn(1000))*
// time.Millisecond)
//time.Sleep(100*time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3() for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) }
}
}

输出,可以看到成片的c2中有一个c1

来自c2:
来自c2:
来自c2:
来自c1:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:

重点是,有两个5448,这里i是全局变量,每一次取i之后,程序都对代码进行了加1(i++),理论上不应该出现相同的数据; 但实际上在并行处理共享资源的时候都会有这个问题,这个问题暂时跳过,我们先讨论非共享资源的处理; 处理的思路不是加锁,而是前面提到的那个重要理念“不要通过共享内存来通信;通过通信来共享内存。”,我们建立一个channel来处理这个共享资源,这里先跳过这个问题。

[root@itoracle channel3]# go run chan3.go > /tmp/a.txt
^Z
[]+ Stopped go run chan3.go > /tmp/a.txt [root@itoracle channel3]# cat /tmp/a.txt |grep c1 |wc -l [root@itoracle channel3]# cat /tmp/a.txt |grep c2 |wc -l

之所以成片,是因为计算机处理速度太快了,将结果输出到文本中进行统计c1与c2的数量差不多,而对于计算机来说,一个很短的时间片却可以处理很多工作

我们在发送数据的时候等待1毫秒,输出结果就不成片连接了

package main

import (
"fmt"
//"math/rand"
//"time"
) var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//time.Sleep(time.Duration(rand.Intn(1000))*
// time.Millisecond)
time.Sleep(*time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
var c1,c2 = channel3(),channel3() for{
//select可以在channel为nil的时候就去接收数据,
//哪个channel有数据发送过来就进行接收
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n) }
}
}

输出

来自c1:
来自c2:
来自c2:
来自c1:
来自c1:
来自c2:
来自c2:
来自c1:
来自c1:
来自c2:
来自c2:
来自c1:
来自c1:
来自c2:
来自c2:
来自c1:
来自c1:
来自c2:
来自c2:

未完,准备出去吃个午饭……

select只收不发会怎么样

package main

import (
"fmt"
) func channel3() chan int{
out := make(chan int)
go func(){
i :=
out <- i
i++
close(out)
}()
return out
} func main(){
var c1,c2 = channel3(),channel3() for{
select {
case n:= <- c1:
fmt.Println("来自c1:",n) case n:= <- c2:
fmt.Println("来自c2:",n)
}
}
}

输出,除了第一次输出正常外,之后的全输出的是0

来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:
来自c2:

这意味着,如果发送方不是一直在发送数据的话,接收方就会输出0;我们必须在接收方处理这个问题。当有数据发送的时候,接收方才接收。

select不是一直发数据,接收会怎么处理

package main

import (
"fmt"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3()
i := //创建一个channel,并接收数据
wk := createWorker(i)
for{
i++
fmt.Printf("第%d次 \n",i)
select {
case n:= <- c1:
wk <- n case n:= <- c2:
wk <- n
}
}
}

输出,不是每次for循环中select都能收到数据,没有收到数据时,就跳过了for循环

第13次
worker 接收的信息为
第14次
worker 接收的信息为
worker 接收的信息为
第15次
第16次
worker 接收的信息为
worker 接收的信息为
第17次
第18次
worker 接收的信息为
worker 接收的信息为
第19次
第20次
worker 接收的信息为
worker 接收的信息为
第21次
第22次
worker 接收的信息为
worker 接收的信息为

这里解读一下这段代码的意思,为后面更复杂的程序作铺垫

select {
case n:= <- c1:
wk <- n case n:= <- c2:
wk <- n
}

在select的代码块中,如果c1 这个channel有数据发送过来,那么就将数据赋值给变量n;如果c2这个channel有数据发送过来,那么就将数据赋值给变量n;n是一个channel,然后又将这个channel传递给了wk(wk也是一个channel),由wk负责将传递过来的数据输出; 如果有一轮for循环中(一个时间 片段中),c1和c2都没有数据传递过来,那么就轮空或者阻塞。这里是两个channel往一个channel中写数据。

select中不仅可以channel输出,还可以有channel输入

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3()
i := //创建一个channel,并接收数据
wk := createWorker(i)
hasDAta := false
n :=
for{
i++
fmt.Printf("第%d次 \n",i) var datawk chan<- int //nil
if hasDAta {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
} select {
case n = <- c1:
hasDAta = true //有数据来了
case n = <- c2:
hasDAta = true //有数据来了
case datawk <- n:
hasDAta = false//数据被转移走了(被输出了)
}
}
}

输出

第1次
第2次
第3次
worker 接收的信息为
第4次
第5次
worker 接收的信息为
第6次
第7次
worker 接收的信息为
第8次
第9次
worker 接收的信息为
第10次
第11次
worker 接收的信息为
第12次
第13次
worker 接收的信息为

channel是一种缓冲,是一种通信缓存;张三向李四抛了一个鸡蛋,如果李四还没准备好接这个鸡蛋,那么鸡蛋就会摔碎在地上,当然张三没有这么笨,他会等李四准备好之后再抛;如果有一个缓冲,张三不用管李四是否准备好,张三有鸡蛋就往缓冲里放,李四发现缓冲有鸡蛋就去取,以通信的方式来达到资源共享,这就是channel。

channel中如果发送数据的速度大于接收的速度,则数据丢失

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
time.Sleep(time.Second)
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3()
i := //创建一个channel,并接收数据
wk := createWorker(i)
hasDAta := false
n :=
for{
i++
fmt.Printf("第%d次 \n",i) var datawk chan<- int //nil
if hasDAta {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
} select {
case n = <- c1:
hasDAta = true //有数据来了
case n = <- c2:
hasDAta = true //有数据来了
case datawk <- n:
hasDAta = false//数据被转移走了(被输出了)
}
}
}

接收时等待1秒再接收数据,发送是连接发送0,1,2,3,……,输出结果中显示很多数据丢失了

第1次
第2次
第3次
第4次
第5次
第6次
第7次
worker 接收的信息为
第8次
第9次
第10次
第11次
第12次
worker 接收的信息为
第13次
第14次
第15次
第16次
第17次
第18次
第19次
worker 接收的信息为
第20次

数据的丢失,并不是因为channel没有输出,而是我们在等待1秒后去打印n的时候,已经是n被多次赋值后的结果了,我们并不是n每次变化就打印n; 同时也表示轮空并不是n没有被赋值,channel没有输出。

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
time.Sleep(time.Second)
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3()
i := //创建一个channel,并接收数据
wk := createWorker(i)
hasDAta := false
n :=
for{
i++
fmt.Printf("第%d次 \n",i) var datawk chan<- int //nil
if hasDAta {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
} select {
case n = <- c1:
hasDAta = true //有数据来了
fmt.Printf("c1 n=%d \n",n)
case n = <- c2:
hasDAta = true //有数据来了
fmt.Printf("c2 n=%d \n",n)
case datawk <- n:
hasDAta = false//数据被转移走了(被输出了)
}
}
}

输出

第1次
c1 n=
第2次
第3次
c2 n=
第4次
c1 n=
第5次
c2 n=
第6次
c1 n=
第7次
worker 接收的信息为
第8次
c2 n=
第9次
c1 n=
第10次
c2 n=
第11次
c1 n=
第12次
worker 接收的信息为
第13次
c2 n=
第14次

为channel接收方加上缓存处理

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
time.Sleep(time.Second)
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3()
i := //创建一个channel,并接收数据
wk := createWorker(i)
n := var values []int
for{
i++
fmt.Printf("第%d次 \n",i) var datawk chan<- int //nil
var currentValue int
if len(values) > {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
currentValue = values[]
} select {
case n = <- c1:
values = append(values,n)
case n = <- c2:
values = append(values,n)
case datawk <- currentValue: //消费掉列表中的第一个值
values = values[:] //列表中的数据前移一位
}
}
}

输出

第31次
第32次
worker 接收的信息为
第33次
第34次
第35次
第36次
worker 接收的信息为
第37次
第38次
第39次
第40次
第41次
worker 接收的信息为
第42次
第43次
第44次
第45次
worker 接收的信息为
第46次

这样可以看到接收到的数据是连接的,不再断续;轮空代表着select循环的次数,也代表着计算机最快的处理速度,之后的程序不再输出这个。

设置channel超时时间

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
time.Sleep(time.Second)
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3() //创建一个channel,并接收数据
wk := createWorker()
n := //时间channel
end := time.After(*time.Second)
i :=
var values []int
for{ var datawk chan<- int //nil
var currentValue int
if len(values) > {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
currentValue = values[]
} select {
case n = <- c1:
values = append(values,n)
case n = <- c2:
values = append(values,n)
case datawk <- currentValue: //消费掉列表中的第一个值
values = values[:] //列表中的数据前移一位 //一定时间内程序未接收到数据就提示timeout
case <- time.After( * time.Millisecond):
i++
fmt.Println("time out ",i)
case <- end:
fmt.Println("game over")
return
}
}
}

输出

time out
worker 接收的信息为
worker 接收的信息为
time out
worker 接收的信息为
worker 接收的信息为
worker 接收的信息为
worker 接收的信息为
worker 接收的信息为
time out
worker 接收的信息为
worker 接收的信息为
game over

显出channel缓存队列数据积压程度

package main

import (
"fmt"
"math/rand"
"time"
) func createWorker(id int) chan<- int{
c := make(chan int)
//创建channel之后,就将接收处理放入了goroutine中
go doWork(id,c)
return c
} func doWork(id int,c chan int){
for n := range c {
time.Sleep(time.Second)
fmt.Printf("worker %d 接收的信息为 %d\n",id,n)
}
} var i int =
func channel3() chan int{
out := make(chan int)
go func(){
for {
//随机停止0-1秒的时间,即发送方不是一直都在发数据,注意不是不再发了(即不是close),只是没有一直发
time.Sleep(time.Duration(rand.Intn())*
time.Millisecond)
out <- i
i++
}
}()
return out
} func main(){
//创建两个channel,并发送数据
var c1,c2 = channel3(),channel3() //创建一个channel,并接收数据
wk := createWorker()
n := //时间channel
end := time.After(*time.Second)
i :=
var values []int //Tick会每过一个时间间隔,发送一个值到channel
tick := time.Tick(*time.Second)
for{ var datawk chan<- int //nil
var currentValue int
if len(values) > {//当有数据时,再对datawk初始化,这样datawk就能够输出数据
datawk = wk
currentValue = values[]
} select {
case n = <- c1:
values = append(values,n)
case n = <- c2:
values = append(values,n)
case datawk <- currentValue: //消费掉列表中的第一个值;如果该条件被轮空跳过,那么数据会写入队列,而不是因覆盖而消失,所以数据不会丢;并且当前值永远是队列的第一个值,也不会变
values = values[:] //列表中的数据前移一位;只有消费一次,当前值才会变化,不因每次for循环datawk都会被置为nil而出现误差。 case <- tick:
fmt.Println("队列当前长度为:",len(values)) //一定时间内程序未接收到数据就提示timeout
case <- time.After( * time.Millisecond):
i++
fmt.Println("time out ",i)
case <- end:
fmt.Println("game over")
return
}
}
}

输出

time out
worker 接收的信息为
队列当前长度为:
worker 接收的信息为
worker 接收的信息为
队列当前长度为:
worker 接收的信息为
worker 接收的信息为
队列当前长度为:
worker 接收的信息为
worker 接收的信息为
队列当前长度为:
worker 接收的信息为
worker 接收的信息为
game over

基础条件:

case的条件谁先成立,就先执行谁;同时成立,则按一定算法处理,可简单认为随机处理。

这次未被处理的条件,下次条件必定为真,但若同时还有为真的条件,它未必就一定会优先处理,依然有一定随机性。

这是前面数据丢失的原因,正在基于这一点,后面设计了缓存队列。

case在进行条件判断的同时,也是一次数据处理,将currentValue传入datawk channel,而datawk chanel中一旦有数据,就会进行处理,这里是打印输出。

case datawk <- currentValue

原理

func createWorker(id int) chan<- int,这一步创建一个通道,并将这个通道的地址返回给主程序,同时,开启一个goroutine尝试从这个通道取数据;主程序就可以不断地向这个通道存放数据,不存放时候,获取数据的goroutine将处理阻塞状态。

其他

wk <- 1 为一个channel赋值,然后goroutine再对其处理,处理的过程是不会阻塞主程序的执行的,可能会出现goroutine还没处理完,主程序就已经执行完的情况。

文件写:

为每个文件创建一个列表

多个channel往列表中放数据

每个列表有一个channel负责从列表中取出数据写入文件

2.7 Go channel的更多相关文章

  1. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

  2. TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...

  3. GO语言之channel

    前言: 初识go语言不到半年,我是一次偶然的机会认识了golang这门语言,看到他简洁的语法风格和强大的语言特性,瞬间有了学习他的兴趣.我是很看好go这样的语言的,一方面因为他有谷歌主推,另一方面他确 ...

  4. Critical: Update Your Windows Secure Channel (cve-2014-6321,MS14-066)

    前言:风雨欲来山满楼,下半年开始各种凶猛的漏洞层出不穷,天下已经不太平,互联网已经进入一个新的台阶 0x01 cve-2014-6321 11月的补丁月,微软请windows的用户吃了顿大餐,发布了1 ...

  5. JAVA NIO Channel

    Basic:   多数通道都是链接到开发的文件描述符的.Channel类提供维持平台独立性的抽象过程.   通道是一种途径,访问和操作操作系统,缓冲区是数据操作点: Channel类继承结构图: 通过 ...

  6. channel Golang

    Golang, 以17个简短代码片段,切底弄懂 channel 基础 (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的 ...

  7. go channel

    channel 是go语言中不同goroutine之间通信一种方法 //定义一个channel c := make(chan bool) //向channel中写入 c <- true //读取 ...

  8. [bigdata] flume file channel CPU消耗比 memory channel高的原因

    https://www.quora.com/Why-does-flume-take-more-resource-CPU-when-file-channel-is-used-compared-to-wh ...

  9. 图解Netty之Pipeline、channel、Context之间的数据流向。

    声明:本文为原创博文,禁止转载.       以下所绘制图形均基于Netty4.0.28版本. 一.connect(outbound类型事件)  当用户调用channel的connect时,会发起一个 ...

  10. go:channel(未完)

    注:1)以下的所有讨论建立在包含整形元素的通道类型之上,即 chan int 2)对于“<-”我的理解是,它可能是一个操作符(接收操作符),也  可能是类型的一部分(如“chan<- in ...

随机推荐

  1. C++11之Lambda特性探析

    目录 目录 1 1. 什么是Lambda? 1 2. 语法格式 1 2.1. 语法格式 1 2.2. 最简定义 2 3. 应用示例 2 4. capture列表 3 4.1. 基本形式 3 4.2.  ...

  2. 编写高质量代码改善C#程序的157个建议——建议77: 正确停止线程

    建议77: 正确停止线程 开发者总尝试对自己的代码有更多的控制.例如,“让那个还在工作的线程马上停止下来”.然而,并非我们想怎样就可以怎样的,这至少涉及两个问题. 第一个问题 正如线程不能立即启动一样 ...

  3. JavaScript - this详解 (二)

    用栗子说this Bug年年有,今年特别多 对于JavaScript这么灵活的语言来说,少了this怎么活! function 函数 this 对于没有实例化的function,我们称之为函数,即没有 ...

  4. linux mysql 权限

    原文地址:http://www.cnblogs.com/eczhou/archive/2012/07/12/2588187.html Linux下mysql新建账号及权限设置 1.权限赋予 说明:my ...

  5. Visual Studio Error

    Visual Studio Error 注意:文中所有“系统”用词,均指Windows Console操作系统IO Debug Error 错误类型 #0表示调用约定错误 可以考虑在指针前面加上_st ...

  6. 高德地图之c#后台获取一个或多个起点到单个终点的直线距离

    首先我们需要一个控制台添加一个新Key(可使用服务选择Web服务,测试的时候IP白名单先不填); 直线距离是通过后台get方式请求API服务地址http://restapi.amap.com/v3/d ...

  7. Python【读取文件,第一行与最后一行】

    文件小的读取方法 with open("a1.txt","r",encoding="gbk") as f: r = f.readlines( ...

  8. iOS去除api过期警告提示

    1.问题描述 应用最低支持版本调高,导致部分旧的代码中API出现警告. 2.解决问题 使用以下代码夹住过期的API部分代码即可解决该问题. #pragma clang diagnostic push ...

  9. OCP2018最新题库,052新题库及答案整理-25题

    25.Which is true about logical and physical database structures? (Choose the best answer) A. An undo ...

  10. “全栈2019”Java第四十二章:静态代码块与初始化顺序

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...