Go之路(二十五):Channel

Channel

协程之间的通信有两种方式

1.全局变量和锁

2.channel

上个章节中用到的就是全局变量和锁的方式来操作一个公共数据

channel的概念

channel已经实现了线程安全,不需要额外加锁了

channel的基本用法:

package main

import(
	"fmt"
)

type Student struct{
	Name string
	Age int
}



func main()  {
	var intChan chan interface{}
	intChan = make(chan interface{}, 10)
	stu1 := Student{
		Name:"tom",
		Age: 13,
	}
	intChan <- stu1

	stu2 := <- intChan
	var stu3 interface{}
	stu3, ok := stu2.(Student)
	if !ok{
		fmt.Println("NO")
	}
	fmt.Println(stu3)
}

  虽然channel需要指明类型,但是如果指定为空接口的话就可以传输任意类型的数据了

另外可以用断言来转换数据

 

例子2:判断一万以内的质数

package main

import(
	"fmt"
	"time"
)

func cal(a chan int, result chan int){
	for v := range a{
		flag := true
		for i:=2;i<v;i++{
			if v%i==0{
				flag = false
				break
			}
		}
		if flag{
			result <- v
		}
	}
}


func main()  {
	var task = make(chan int,100)
	var result = make(chan int,100)
	go func (){
		for i:=0;i<10000;i++{
		task <- i
		}
		close(task)
	}()

	for i:=0;i<4;i++{
		go cal(task,result)
	}

	go func(){
		for v:= range result{
			fmt.Println(v)
		}
	}()
	time.Sleep(time.Second*5)
}

  1.管道可以关闭,关闭后不能继续放值

  2.可以用for range语句来取管道内的值

  3.可以用ok来判断管道是否关闭

  

v,ok := <-task
      if ok == false{
               fmt.Println("管道已经关闭")  
}  

  如果for{}不加条件,然后里面是

v,ok := <-task,就会出现下面的情况

不关闭管道一直取值取到空的时候是会阻塞的,但是一旦关闭管道,取完值后再继续取值就会一直取到0(空值)

  但是如果是for range 取完就自动退出了

 

可以再用一个管道来确认目标协程是否执行完

例子:

package main

import(
	"fmt"
	"math/rand"
)

func cal(a chan int, result chan int,exist chan int){
	for v := range a{
		flag := true
		for i:=2;i<v;i++{
			if v%i==0{
				flag = false
				break
			}
		}
		if flag{
			result <- v
		}
	}
	fmt.Println("exit")
	exist <- rand.Intn(10)
}


func main()  {
	var task = make(chan int,100)
	var result = make(chan int,100)
	var exist = make(chan int,4)
	go func (){
		for i:=0;i<10000;i++{
		task <- i
		}
		close(task)
	}()

	for i:=0;i<4;i++{
		go cal(task,result,exist)
	}

	go func (){for i:=0;i<4;i++{
			b := <- exist
			fmt.Println(b)
		}
		close(result)
	}()
	
	
	for _= range result{
		// fmt.Println(v)
	}

}

  但是要注意这个确认的需要另外起一个协程,因为go的自检测机制会认为这个是死锁

 

 

非阻塞以及多路复用可以用select

select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。

select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。

例子:

package main

import(
	"fmt"
	"time"
)

func main()  {
	var a = make(chan int,5)
	var b = make(chan int,5)
	
	go func(){
		var i int
		for {
			a <- i
			time.Sleep(time.Second)
			b <- i*i
			time.Sleep(time.Second)
			i++
		}
	}()

	for{
		select{
		case v := <- a:
			fmt.Println(v)
		case v:= <- b:
			fmt.Println(v)
		default:
			time.Sleep(time.Second)
			fmt.Println("?")
		}
	}
}

  只读 <-chan  只写chan<-

posted @ 2018-12-08 15:39  __Miracle  阅读(259)  评论(0编辑  收藏  举报