[TOC]

channle 都是应用于两个go程,一个读,一个写

  • channel 具有阻塞的作用,类似go程之间对同步资源进行锁机制
  • 具有数据传递的功能

一、无缓冲channel —-(同步)

无缓冲channel 定义
  • var ch =make(chan string ) 或者ch :=make(chan string)
无缓冲channel的说明
  • ch <- “hehe” 写数据,数据没有被在读,则为阻塞
  • <-ch 读数据,数据没有没有在写, 则为阻塞
  • len(ch) :channel中未读取的元素个数,cap(ch):channel中通道的容量 。 都是零

二、有缓冲channel ——(异步)

有缓冲channel定义
  • var ch =make(chan string ,3) 或者ch :=make(chan string ,3)
  • 当存储的元素个数超过了cap(ch) 才会阻塞

三、关闭channel

  • close(ch) ,不再向对端发送数据

  • 判断chan是否关闭 ,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    if num,ok:=<-chan; ok ==true{
    fmt.Println("已经关闭")
    }



    // 无缓冲 关闭channle,读端 ok为false,num为0
    // 如果channel已经关闭 ,写端出现panic send on closed channel
    // 如果channle有缓冲,当len(ch) 的值没有超过cap(ch),那么close(ch)是阻塞的
    // 如果channle无缓冲,当ch中存在元素,close(ch)也是阻塞的
    func main() {
    ch:=make(chan int)

    go func() {
    for i:=0;i<=100;i++{
    ch <-i
    }
    close(ch) //这里close其实是阻塞的

    }()

    go func() {
    for{
    if num,ok:=<-ch;ok{
    fmt.Print(num)
    close(ch)
    }else{
    fmt.Println("jieshu",num)
    }
    }
    }()

四、单向channel

  • 双向channel ch:=make(chan string )

  • 单项写channel var readCh chan<- int

  • 单向读channel var writech <-chan

单向channel和双向channel转换
  • 双向转单向 readch = ch
  • 单向不可以转双向
传参:传【引用】

Channel通信的时候,要保证channle不能在同一个go程之中

五、消费者,生产者模型

  • 解耦(降低消费者和生产者直接的耦合度)
  • 并发(消费者,生产者消费不对等,能保持正常的通信)
  • 缓存(生产者和消费者处理不一致时,暂存数据)
生产者,消费者实现:
  -  有缓冲的channel :异步同行
  -   无缓冲的channel:同步通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import "fmt"

type Orderinfo struct {
id int
price int
}

func producer(ch chan<- Orderinfo) {
for i:=0;i<=1000;i++{

ch<-Orderinfo{id:i+1,price: 10}
}
close(ch)
}

func consumer(ch <-chan Orderinfo) {
for order:=range(ch){
fmt.Println("订单价格:",order.price,"订单编号:",order.id)
}
}


func main() {
ch :=make(chan Orderinfo)


go producer(ch)
consumer(ch)
}

六、定时器: 三种定义方法

time.sleep() ; time.NewTimer() ;time.After()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"time"
)

func main() {
//time.sleep定时
fmt.Println("当前时间",time.Now())
time.Sleep(time.Second*1)
fmt.Println("time.sleep定时当前时间",time.Now())


//time.Newtimer
// 创建定时器
t:=time.NewTimer(time.Second*1)
now:=<-t.C
fmt.Println("time.NewTimer阻塞 t.C管道读取系统当前时间为:",now)
//time.After 返回一个管道
now=<-time.After(time.Second)
fmt.Println("time.After,系统chan写满,则返回当前时间:",now)

}

七、select 语句

select (退出for 循环中的select ,用return 或者goto的方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func main()  {
ch:=make(chan int)
quit:=make(chan bool)
fmt.Println(<-time.After(time.Second))
go func() {
for{
select {
case num:=<-ch:
fmt.Println("nume",num)

case <-time.After(time.Second*3):
fmt.Println("超时")
quit<-true
//return
goto loop
}
}
loop:
fmt.Println("for end")
}()

for i:=0;i<4;i++{
ch<-i
time.Sleep(time.Second)
}
<-quit


}

八、锁

golang死锁模型:
  • 1、单go程自己死锁: channel至少在两个go程中使用,否则思索
  • 2、go程间channel访问顺序:使用channle一端写(读),要保证另一一段读(写),同时有机会执行,否则思索
  • 3、多go程,多channel交叉死锁
  • 4、在go语言中,尽量不要将互斥锁和channel一起使用,会造成隐形死锁
golang 锁
  • 1.channel 定义互斥锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义个全局channel
var ch =make(chan string)

func Printer(s string){
for _,s1:=range(s){
fmt.Print(string(s1))
<-time.After(time.Microsecond*500000)
}

}

func main() {
//go程里结束,向ch中传递
go func() {
Printer("hello")
ch<-"ok"
close(ch)
}()
// 主go程在阻塞等待
<-ch
Printer("world")

}
  • 2.用锁定义互斥锁(这种锁,我们一般称为:–>建议锁)

    1
    2
    3
    4
    5
    6
    var mutex sync.Mutex  //定义锁

    //对共享资源枷锁
    mutex.Lock()
    fmt.Pirnt('hello')
    muter.Unlock()
  • 3.读写锁: 读时共享,写时独占(写比读优先级要高)

    • var rwmutex2 sync.RWMutex 定了一把锁,这个锁带有读和写的属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    var rwmutex2 sync.RWMutex

    var I int
    func reader2(i int){
    for{
    // 读锁,并发共享
    rwmutex2.RLock()
    a:=I
    rwmutex2.RUnlock()
    fmt.Printf("=======第%d个读进程,读取到%d\n",i,a)
    time.Sleep(time.Second)
    }

    }

    func writer2(a int){
    for{

    num:=rand.Intn(100)
    // 写锁,独占
    rwmutex2.Lock()
    I=num
    fmt.Printf("第%dth写go程,写数据:%d \n" ,a,num)
    time.Sleep(time.Second*2)
    rwmutex2.Unlock()
    }
    }

    func main(){

    for i:=0;i<=3;i++{
    go reader2(i)
    }

    for i:=0;i<=3;i++{
    go writer2(i)
    }

    for{
    ;
    }

    }
    • channel 无法完成共享读,因此需要对读完成共享数据读的时候,使用读写锁
条件变量 type Cond struct{}
  • 适用生产者消费者模型:本身不是锁,经常要与锁结合使用,他是一个结构体

  • 作用:

    • 加锁
    • 访问公共区域(比如带缓冲channle)
    • 解锁
    • 唤醒阻塞在条件变量上对端
  • 条件变量Cond常见方法:

    • wait() 函数 使用场景

      • 阻塞等待条件变量满足
      • 释放已掌握的互斥锁相当于 cond.L.Unlock() ;注意:两步分为原子操作 <—-及不可以分开
      • 当被唤醒,Wati()函数返回时候,解决阻塞并重新获取互斥锁。相当于cond.L.Lock()
    • cond.Single() 唤醒对端

    • cond.Broadcast()惊群唤醒

  • 创建条件变量流程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //1.创建条件变量
    var cond sync.Cond
    //2.创建条件变量指定锁
    cond.L=new(sync.Mutex)

    //3. 给公共区加锁
    cond.L.Lock()
    //4.判断是否达到阻塞条件
    //
    //4.1 producer 端判断条件
    for len(ch)==cap(ch){
    cond.Wait() //1.阻塞2.解锁 3.加锁
    }
    //4.2 consumer端判断
    for len(ch)==0{
    cond.Wait()
    }

    //5.访问公共区,读写,打印

    //6.对公共区条件变量解锁
    cond.L.Unlock()

    //7.唤醒阻塞在条件变量对端
    cond.Single()

    【条件变量示例】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    package main

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

    //1.创建条件变量
    var cond sync.Cond

    func producer2(product chan<- string,index int) {
    //3.使用条件变量给公共区加锁
    cond.L.Lock()
    //4. 判断是否已经填满缓冲区,如果填满则wait()
    for len(product)==cap(product){
    cond.Wait()
    }
    //5.执行写操作
    str:=fmt.Sprintf("produce%d",index)
    product<-str
    fmt.Printf("这是第%d个producer进程,produce %q \n",index,str)
    time.Sleep(time.Millisecond*200)
    // 6.解锁
    cond.L.Unlock()
    //7.唤醒阻塞的对端
    cond.Signal()

    }

    func consumer2(product <-chan string,index int) {

    for{

    cond.L.Lock()
    for len(product)==0{
    cond.Wait()
    }
    str :=<-product

    fmt.Printf("--------这是第%d个consumer进程,consum %s \n",index,str)
    cond.L.Unlock()
    cond.Signal()
    }
    }



    func main() {

    product:=make(chan string,3)

    // 2.指定条件变量用的锁为互斥锁
    cond.L=new(sync.Mutex)

    for i:=1;i<=1000;i++{
    go producer2(product,i)
    }

    for i:=1;i<=3;i++{
    go consumer2(product,i)
    }

    for{
    ;
    }
    }