1、channel:
goroutine和goroutine的双向通道就是channel
func chanDemo(){
//var c chan int //c==nil,用var定义的chan,等于nil的channel用不了
c := make(chan int) //通常使用make来创建channel,可以直接使用
c <- 1 //会死锁,发了数据没人收
c <- 2
n := <-c
fmt.Println(n)
}
-----------------------------------------------------------------------
func chanDemo(){
//var c chan int //c==nil,用var定义的chan,等于nil的channel用不了
c := make(chan int) //通常使用make来创建channel,可以直接使用
go func(){
for { //开一个goroutine不断的在收
n := <-c
fmt.Println(n)
}
}()
c <- 1 //会死锁,发了数据没人收
c <- 2
time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}
-------------------------提取----------------------------------------------
演示channel作为参数、以及数组
func worker(id int,c chan int){
for { //开一个goroutine不断的在收
n := <-c
fmt.Printf("Worker %d received %d\n",id,n)
}
}
func chanDemo(){
var channels [10]chan int //建立channel的数组
for i:=0;i<10;i++{
channels[i] := make(chan int) //通常使用make来创建channel,可以直接使用
go worker(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)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}
-------------------------演示channel作为返回值----------------------------------------------
//chan是用来送数据的
func createWorker(id int) chan<- int {
c := make(chan int)
go func() {
for { //开一个goroutine不断的在收
n := <-c
fmt.Printf("Worker %d received %d\n",id,n)
}
}()
return c
}
//<-chan收数据
func chanDemo(){
var channels [10]chan<- int //建立channel的数组
for i:=0;i<10;i++{
channels[i] := createWorker(i) //通常使用make来创建channel,可以直接使用
}
for i:=0;i<10;i++{
channels[i] <- 'a'+ i
}
for i:=0;i<10;i++{
channels[i] <- 'A'+ i
}
time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}
------------------bufferedChannel------------------------------
func bufferedChannel(){
c := make(chan int,3) //缓冲区
go worker(0,c)
c <- 1
c <- 2
c <- 3
time.Sleep(time.Millisecond)
}
//将worker提取出来
func worker(id int,c chan int) {
for { //开一个goroutine不断的在收
n,ok := <-c //如果没有值,退出循环
if !ok {
break
}
fmt.Printf("Worker %d received %d\n",id,n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id,c)
return c
}
//告诉别人数据发完了,发送方close
func channelClose(){
c := make(chan int)
go worker(0,c)
c <- 1
c <- 2
c <- 3
close(c)
time.Sleep(time.Millisecond)
}
//接收方有两种方法处理close的channel
1、用ok标示;
2、用range
//或者用range获取channel的值
func worker(id int,c chan int) {
for n := range c {
fmt.Printf("Worker %d received %d\n",id,n)
}
}
理论基础:Communication Sequential Process(CSP)
不要通过共享内存来通信;通过通信来共享内存
2、使用channel等待任务结束:
代码改动有点大
func doWork(id int,c chan int,done chan bool) {
for n := range c {
fmt.Printf("Worker %d received %d\n",id,n)
//done <- true //直接这样,容易造成阻塞,等两次,因为一直发,需要有人收才行,简单的方法是再开一个协程
go func(){ done <- true }() //并行地发done
}
}
//建立一个结构
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 doWork(id,w.in,w.done)
return w
}
func chanDemo(){
var workers [10]worker //之前是channel的数组
for i:=0;i<10;i++{
workers[i] := createWorker(i) //通常使用make来创建channel,可以直接使用
}
for i,worker := range workers{
worker.in <- 'a'+ i
//<-worker.done
}
for i,worker := range workers{
worker.in <- 'A'+ i
//<-worker.done
}
//wait for all of them
for _,worker := range workers {
<-worker.done
<-worker.done
}
//workers[i]done的引入可以删掉sleep
//time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}
------------用waitGroup的方式替换done的方法------------------------------
func doWork(id int,c chan int,wg *sync.WaitGroup) {
for n := range c {
fmt.Printf("Worker %d received %d\n",id,n)
wg.Done()
}
}
func createWorker(id int,wg *sync.WaitGroup) worker {
w := worker{
in := make(chan int),
wg: wg,
}
go doWork(id,w.in,wg)
return w
}
//建立一个结构
type worker struct{
in chan int
wg *sync.WaitGroup //使用指针
}
func chanDemo(){
var wg sync.WaitGroup
var workers [10]worker //之前是channel的数组
for i:=0;i<10;i++{
workers[i] := createWorker(i,&wg) //通常使用make来创建channel,可以直接使用
}
wg.Add(20)
for i,worker := range workers{
worker.in <- 'a'+ i
//<-worker.done
}
for i,worker := range workers{
worker.in <- 'A'+ i
//<-worker.done
}
wg.Wait()
}
-----------------------再次包装Done的方法---------------------------------
func doWork(id int,w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %d\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()
},
}
go doWork(id,w)
return w
}
3、使用channel进行任务的遍历:
实现树的遍历,第4章节有关于树的遍历相关代码
func (node *Node) TraverseWithChannel() chan *Node {
out := make(chan *Node)
go func(){
node.TraverseFunc(func(node *Node){
out <- node
})
close(out)
}()
}
func main(){
//省略结点的创建
c := root.TraverseWithChanel()
maxNode := 0
//统计最大的结点数字
for node := range c {
if node.Value > maxNode {
maxNode = node.Value
}
}
}
4、用select进行调度:
func main(){
var c1,c2 chan int
//c1和c2同时收,谁来的快就优先处理谁,就用select
//非阻塞式获取
select {
case n := <-c1:
fmt.Printf("Received from c1:",n)
case n := <-c2:
fmt.Printf("Received from c2:",n)
default:
fmt.Printf("No value received")
}
}
------------------------------------------------
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++
}
}()
}
func main(){
var c1,c2 chan int
//w := createWorker(0)
// var w chan int //nil channel在select中是可以运行的,但是不会被select到
var worker = createWorker(0) /利用nil channel 的性质
//c1和c2同时收,谁来的快就优先处理谁,就用select
n :=0
hasValue := false
for {
var activeWorker chan<- int
if hasValue {
activeWorker = worker
}
//非阻塞式获取
select {
case n := <-c1:
hasValue = true
//w <- n
case n = <-c2:
hasValue = true
//w <- n
case activeWorker <-n:
hasValue = false
//case w <- n :
//if hasValue {
//send to w
//}
}
}
}
//如果消耗速度太慢,上述n会被覆盖掉
func main(){
var c1,c2 chan int
var worker = createWorker(0) //利用nil channel 的性质
var values []int //存起来
//10秒后结束
tm := time.After(10*time.Second)
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
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): //每次select花费的时间
fmt.Println("timeout")
case <-tm:
fmt.Println("bye")
return
}
}
}
----------------定时查看----------------------
func main(){
var c1,c2 chan int
var worker = createWorker(0) //利用nil channel 的性质
var values []int //存起来
//10秒后结束
tm := time.After(10*time.Second)
tick := time.Tick(time.Second)
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
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): //每次select花费的时间
fmt.Println("timeout")
case <-tick: //定时器
fmt.Println("queue len =",len(values))
case <-tm: //总时间确定程序运行时长
fmt.Println("bye")
return
}
}
}
select的使用:用default就不会被阻塞
定时器的使用
在select中使用nil channel
5、传统同步机制:
上节是通过channela进行通信,没有使用到锁
传统同步机制:
1、WaitGroup
2、Mutex
3、Cond
传统的同步机制是共享内存来进行通信
//原子,会引发冲突
//type atomicInt int
//改一下结构,包含锁以解除竞争
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increment(){
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}
func (a *atomicInt) get() int{
a.lock.Lock()
defer a.lock.Unlock()
//return a.value
}
func main(){
var a atomicInt
a.increment()
go func(){
a.increment()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get())
}
用命令行go run -race atomic.go 会提示冲突
//需要对加锁的数据
func (a *atomicInt) increment(){
//只有在匿名函数中,defer才会起作用,defer控制在函数体里面
func(){
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}()
}