golang学习笔记之channel

1.声明与初始化

func main() {
	// 声明 var 变量名 chan 数据类型
	var c chan int

	fmt.Printf("chan c : %v
", c)

	// 初始化 make(chan 数据类型 [, int])
	c = make(chan int)
	fmt.Printf("chan c : %v
", c)
	time.Sleep(time.Second * 5)

	// 也可以这样声明和初始化
	//ch := make(chan string, 2)
	//ch <- "hello"
	//ch <- "world"
	//s1 := <-ch
	//s2 := <-ch
	//fmt.Println(s1, s2)
}

  

  

2.入队 出队

var a chan int

入队:  a <- 100

出队: data := <- a

3.无缓冲区channel

初始化make时不指定容量.即: 

func main() {
	// 声明 var 变量名 chan 数据类型
	var c chan int

	fmt.Printf("chan c : %v
", c)

	// 初始化一个无缓冲区channel
	c = make(chan int)
	fmt.Printf("chan c : %v
", c)
	time.Sleep(time.Second * 5)
}

  

无缓冲区channel只有在读取的时候才能写入值

package main

import (
	"fmt"
	"time"
)

func produce(c chan int) {
	c <- 1000
}

func consume(c chan int) {
	data := <-c
	fmt.Printf("data : %v
", data)
}

func main() {
	// 声明 var 变量名 chan 数据类型
	var c chan int

	fmt.Printf("chan c : %v
", c)

	// 初始化无缓冲区channel
	c = make(chan int)

	// 写入channel
	go produce(c)

	// 消费(读取)channel
	go consume(c)

	time.Sleep(time.Second * 5)
}

  

如果注释掉 go consume(c), 那么将没有消费(读取)channel,程序就会一直阻塞直到主线程结束.(如下节:阻塞channel)

当有go consume(c)进行消费(读取)时,才能写入并读取出channel的值

4.阻塞channel

func main() {
	var a chan int
	if a == nil {
		fmt.Println("channel is nil, going to define it")
		a = make(chan int)
		a <- 10 //将一直阻塞在这里
		fmt.Printf("Type of a is : %T", a)
	}
}

  

带缓冲区与不带缓冲区 区别举例:

1.不带缓冲区:面对面签收快递.无收件人取件时(读取),快递员死等(阻塞,除非主线程结束,如下班)

2.带缓冲区:快递员将包裹存放在柜中(如蜂巢,柜子的数量就是缓冲的容量)

5.使用channel对goroutine进行同步  

package main

import (
	"fmt"
	"time"
)

func hello(c chan bool) {
	time.Sleep(time.Second * 5)
	fmt.Println("只有当我执行后,主线程才能退出哦!")
	c <- true
}

func main() {
	var exitChan chan bool
	exitChan = make(chan bool)
	go hello(exitChan)
	// 只有当子线程中的 channel写入值之后,下面才能读取.否则将一直阻塞
	<-exitChan
	fmt.Println("主线程结束")
}

6.单向channel

var a chan<- int //只写

var b <-chan int //只读

7.关闭channel

close(c)

8.判断关闭及遍历channel数据

a.使用ok判断并使用for循环遍历channel

package main

import (
	"fmt"
	"time"
)

func hello(c chan bool) {
	time.Sleep(time.Second * 5)
	fmt.Println("只有当我执行后,主线程才能退出哦!")
	c <- true
}

func main() {
	var exitChan chan bool
	exitChan = make(chan bool)
	go hello(exitChan)
	// 只有当子线程中的 channel写入值之后,下面才能读取.否则将一直阻塞
	<-exitChan
	fmt.Println("主线程结束")
}

  

b.使用 for range遍历-----推荐方式(自动判断是否关闭)

package main

import "fmt"

func produce(c chan int) {
	for i := 0; i < 10; i++ {
		c <- i
	}
	close(c)
}

func main() {
	ch := make(chan int)
	go produce(ch)

	// 使用for range 对channel数据遍历
	for v := range ch {
		fmt.Println("接收数据: ", v)
	}
}

  

9.带缓冲区channel

package main

import "fmt"

func main() {
	var ch chan string
	ch = make(chan string, 3)

	ch <- "hello"
	ch <- "world"
	ch <- "!"

	//先入先出
	s1 := <-ch
	s2 := <-ch
	s3 := <-ch
	fmt.Println(s1, s2, s3)
}

注:channel空或者满 都会阻塞

10.使用waitgroup等待一组goroutine结束(sync.WaitGroup)

原理:计数.  1.起一个goruntine时计数加一 2.执行完一个gorutine时计数减一 3.当计数为0时结束 

package main

import (
	"fmt"
	"sync"
	"time"
)

func process(i int, wg *sync.WaitGroup) {
	fmt.Println("started gorutine ", i)
	time.Sleep(time.Second * 2)
	fmt.Printf("goruntine %d ended 
", i)
	wg.Done() //执行完waitgroup计数减一
}

func main() {
	// 场景: 需要一组gorutine都执行完 才退出
	var wg sync.WaitGroup
	no := 10
	for i := 0; i < no; i++ {
		wg.Add(1) //起一个gorutine时 计数加一
		go process(i, &wg)
	}
	wg.Wait() //当计数为0时 等待结束
	fmt.Println("All go runtines finished executing")
}

  

原文地址:https://www.cnblogs.com/yin5th/p/9201406.html