Go并发编程(二)Channel

文章目录

Go并发编程(二)Channel

channel通信机制

在go中推荐使用基于消息的并发通信,而不是通过共享内存,通过channel机制,实现goroutine相互通信

Go并发编程(二)Channel

channel是进程内的通讯方式,是不支持跨进程通信的,如果需要进程间通讯的话,可以使用Socket等网络方式。

使用

通过channel机制,实现goroutine相互通信,管道是类型相关的,即一个管道只能传递一种类型的值。管道中的数据是先进先出的。

func workGO(c chan int)  {
	fmt.Println("---协程工作---")
	time.Sleep(10 * time.Second)
	num := <- c
	fmt.Println("接收到的值:",num)
}

func MyChannel(){
	c := make(chan int,10)	// 声明缓冲区大小,接收到的值先发送到缓冲区,不阻塞协程
	defer close(c)	// 关闭通道
	go workGO(c)
	fmt.Println("主线程")
	// 传递2给管道,默认是阻塞式管道,即一定要读取出来后才能继续执行
	c <- 2
	fmt.Println("协程从管道中读取出数值")
	time.Sleep(10 * time.Second)
}

// 单向管道
var ch1 chan<- float64     // 只能向里面写入float64的数据,不能读取
var ch2 <-chan int         // 只能读取int型数据
// 关闭channel,直接调用close()即可
close(ch)
// 判断ch是否关闭,判断ok的值,如果是false,则说明已经关闭(关闭的话读取是不会阻塞的)
x, ok := <-ch

阻塞式:

先阻塞住
Go并发编程(二)Channel

后接收到后解除阻塞

Go并发编程(二)Channel

带缓冲区:

Go并发编程(二)Channel

channel + select实现非阻塞管道通信,接收不到数据则走select default分支,避免阻塞

func send(c chan int)  {
	for i :=1 ; i<10 ;i++  {
		c <-i
		fmt.Println("send data : ",i)
	}
}

func TestSelect()  {
	a := make(chan int)
	b := make(chan int)
	go send(a)
	// time.Sleep(3 * time.Second)
	select{
	case one := <-a:fmt.Println("a:",one)
	case two := <-b:fmt.Println("b:",two)
	default:
		fmt.Println("default")
	}
}

实现原理

在Java中实现线程间通信依靠的是共享内存的方式,采用同步锁来保证共享内存通信的线程安全问题,但是在Go中既可以通过共享内存实现goroutine间通信,也可以基于管道消息实现goroutine通信,并且在Go中更鼓励通过后者的方式。

Go中提出了通信顺序模型CSP模型(Communicating sequential processes),goroutine和channel分别是CSP的通信实体和媒介

Go并发编程(二)Channel

上图中的两个 Goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。

Go中的channel在运行时底层是runtime.hchan结构体:

type hchan struct {
    // channel中元素容量
	qcount   uint           // total data in the queue
    // 循环队列长度
	dataqsiz uint           // size of the circular queue
	// 队列缓冲区数据指针
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	// 元素数量
	elemsize uint16
	// 管道是否已关闭
	closed   uint32
	// 元素类型
	elemtype *_type // element type
	// 发送指针
	sendx    uint   // send index
	// 接收指针
	recvx    uint   // receive index
	// sendq 和 recvq 存储了当前 Channel 由于缓冲区空间不足而阻塞的 Goroutine 列表
	recvq    waitq  // list of recv waiters
	sendq    waitq  // list of send waiters

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	lock mutex
}
上一篇:Golang进阶实战编程(学完基础必看)视频教程


下一篇:387集Go语言核心编程培训视频教材整理 | goroutine和channel(四)