Golang channel

2018-01-30 05:15:53

1、什么是 channel(管道/通道)?

在 golang 中,channel 是多个 goroutine (线程) 之间传递和同步数据的一种手段。同一时刻,仅有一个线程可以向 channel 发送数据,同样的,同一时刻也只能有一个线程能从 channel 读取数据。

channel 的特性使得它可以解决并发编程可能造成的死锁问题,同时也是 Mutex (上锁)及 sync 多线程编程的替代方案,可以简化代码结构。

2、channel 的声明方式

var c chan int  // var 关键字声明,缓冲int类型数据
var c chan string // 缓冲string类型数据
c := make(chan int) //  make关键字声明,非缓冲管道
c := make(chan int, 10) // 缓冲管道
type ChanInt chan int // 设置别名

总结为:

  • 通过 chan 关键字标识为管道
  • 有两种声明方式
  • make 方式可以生成缓冲管道非缓冲管道

3、缓冲 channel 和无缓冲 channel 区别?

channel1 := make(chan int) // 无缓冲
channel2 := make(chan int, 2) // 有缓冲
len(channel1)   // 长度
cap(channel2)   // 容量

无缓冲:
缓冲为1,最多只能缓冲1个数据。无缓冲的读写实际为同步操作,只有当写与读操作匹配时,才会继续,否则会死锁!所以不能同时将读写置于主线程,如果主线程中有读/写操作,必须置于分线程的声明后,以下为三种正确的写法:

c := make(chan int)

// 1
go func() {
    <-
}
c <- 1

// 2
go func() {
    c <- 1
}
<-c

// 3
go func() {
    c <- 1
}
go func() {
    <-c
}
非缓冲的读和写操作必须“相遇”才能执行,否则会**阻塞**。就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走

有缓冲:
最大缓冲等于容量,相当于一个先进先出的队列。有缓冲的管道,只有当管道容量已存满,再未取走值的情况下继续放值时才会发生阻塞。

4、巧用 channel

通过 channel 的某些奇技淫巧,可以替代 sync,简化代码量,如下示例:

1、 在一个非缓冲管道中,依次循环存入值,并逐个取出

sync实现方法:

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup //队列

func main() {

	// 先创建一个非缓冲管道
	c := make(chan int)
	wg.Add(2)
	go func() { //子线程
		for i := 0; i < 10; i++ {
			c <- i //写入数据
			fmt.Println("写入数据")
		}
		wg.Done()
	}()

	go func() {
		for i := 0; i < 10; i++ {
			a := <-c
			fmt.Println(a) //读取数据
			fmt.Println("读取数据")
		}
		wg.Done()
	}()

	wg.Wait()

}

=> 使用 channel 实现:

package main

import "fmt"

func main() {
    c := make(chan int)
    go func() {
        for i:=10; i<10; i++ {
            fmt.Println("------")
            c <- i
        }
        close(c)
    }()
    
    for n := range c {
        fmt.Println("+++++")
        fmt.Println(n)
    }
}

注:for n := range nclose(c) 配合使用,成对出现。for 循环会监视管道c,一旦管道中有数据,立即执行循环中的命令,取出值,直到 close(c) 命令关闭管道,循环结束

2、 等待多个分线程执行完毕再关闭通道——巧用多个 channel

sync 实现方法:

package main

import (
	"fmt"
	"sync"
)

func main() {

	c := make(chan int) //创建通道

	var wg sync.WaitGroup //队列
	wg.Add(2)
	go func() {
		fmt.Println("111111111")

		for i := 0; i < 10; i++ {
			c <- i
		}
		wg.Done()
	}()

	go func() {
		fmt.Println("22222222")
		for i := 10; i < 20; i++ {
			c <- i
		}
		wg.Done()
	}()

	go func() {
		fmt.Println("33333333")
		wg.Wait()
		close(c)
	}()

	for n := range c {

		// <-c  有返回值
		fmt.Println(n)
	}
}

=> channel 解决方案:

package main

import (
	"fmt"
)

func main() {

	c := make(chan int)
	done := make(chan bool)

	go func() {
		for i := 0; i < 10; i++ {
			c <- i
		}
		done <- true
	}()

	go func() {
		for i := 0; i < 10; i++ {
			c <- i
		}
		done <- true
	}()

	go func() {
	   <-done //死锁
	   <-done
	   close(c)
	}

	for n := range c {
		fmt.Println(n)
	}
}

Golang指针

golang中的指针与c语言指针略有不同。 1、基本类型的指针 var num int = 10 var p *int = &num // 指针的类型为所指向的变量的类型前加* 2、数组的指针与指针数组 数组的指针: arr := [3]int{1, 2, 3} var arrp *[3]int // 数组的指针 a := &arr // 直接取数组变量的地址赋值会报错(arrp = &arr) arrp = a // 通过第三个变量传递 指针数组: arr := [3]*int{} numArr := [3]int{1, 2, 3} for i, v

Golang channel进阶

将向管道中写入数据的称为“生产者”,从管道中读取数据的称为“消费者”。 1、生产者与消费者关系 在上篇文章中,生产者与消费者是1:1及n:1的关系,那么能不能实现1:n的关系嘞?即一个生产者向管道添加数据,多个消费者从管道读取?示例如下: 1:2案例: package main import ( "fmt" ) // 生产者 对 消费者 :1 -> 2 func main() { c := make(chan int) done := make(chan bool) // 生产者:大黄 go func() { for i := 0; i < 100;