go语言通道插入0_Go 语言通道

51Reboot 运维开发

Golang 课程

K8S 课程

Python 自动化进阶课程

Python 基础实战课程

运维前端课程

课程试听预约请扫码>>>

68e414962be7dc9daf96fd1dc308f032.png一两种并发模型
  • 多线程共享内存,以共享内存的方式来通信。比如 java 在访问共享数据的时候,通过加锁来实现,java.util.concurrent包。

  • CSP(communicating sequential processes)并发模型,以通信的方式来共享内存。Go 的 CSP 并发模型是通过 goroutine 和 channel 来实现的。

二通道

原子函数和互斥函数都能工作,但是依靠它们都不会让编写并发程序变得更简单,更不容易出错,或者更有趣。在 Go 语言里,你可以使用通道来发送和接收需要共享的资源,在 goroutine 之间同步。

声明通道时,需要指定将要被共享的数据类型,可以以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。

无缓冲的通道:是指接收前没有能力保存任何值的通道,这种类型的通道要求发送  goroutine 和接收 goroutine 都准备好,才能完成发送和接受操作。如果没有同时准备好,回导致先执行发送或接收操作的 goroutine 阻塞等待。

有缓存的通道:是一种指在被接收前能储存一个或多个值的通道,这种类型的通道并不要求 goroutine 之间必须同时完成发送和接收。只有在通道中没有接收的值时,接收动作才会阻塞。只有在通道没有可用缓存区容纳被发送的值时,发送动作才会阻塞。

无缓存通道工作流程图:

99185852f222e2d65c21c9b5078b8491.png

有缓存通道工作流程图:

a7f61dd93f80ab23f4ac547e9092b4b1.png

三代码示例

无缓存代码示例1:

package mainimport "sync"// wg1用来等待程序结束var wg1 sync.WaitGroup// 无缓冲的通道必须配对操作的goroutine出现,否则会一直阻塞func main() {    // 计数加1,表示要等待一个goroutine    wg1.Add(1)    // 创建一个无缓冲的通道    c := make(chan string)    // 启动一个goroutine    go func() {        // 延时调用,在函数退出时调用done()来通知main函数工作已完成。        defer wg1.Done()        // 接收消息        value :=         println(value)    }()    // 发送消息    c "hello"    // 等待完成    wg1.Wait()}

无缓存通道代码示例2,实现阻塞生产者消费者模式:

package mainimport (    "fmt"    "time")// 生产者通过通道发送数据func produce(p chanint) {    for i := 0; i < 10; i++ {        p         fmt.Println("send:", i)    }}// 消费者通过通道接收数据func consumer(c chan int) {    for i := 0; i < 10; i++ {        v :=         fmt.Println("receive:", v)    }}func main() {    // 无缓存数据    ch := make(chan int)    // 生产者给ch赋值后,阻塞,知道消费者取出    go produce(ch)    // 消费者取出数据后,进行下一次循环,阻塞,知道生产者生产数据    go consumer(ch)    // 优先执行主线程,主线程执行完后,立即退出,没有空余时间执行子线程    time.Sleep(time.Second)}

有缓存通道代码示例1:

package mainfunc main() {    // 创建带三个缓冲槽的通道    c := make(chan int, 3)    // 发送两个消息,缓冲槽区未满,不会阻塞    c 1    c 2    // 接收两个消息,缓冲区尚有数据,不会阻塞    println(c)    println(c)}

有缓存通道代码示例2,模拟多人工作:

package mainimport (    "sync"    "fmt"    "math/rand"    "time")const (    // 要使用的goroutine的数量    numberGoroutines = 4    // 要处理的工作的数量    taskLoad = 10)// wg用来等待程序完成var wg2 sync.WaitGroupfunc main() {    // 创建一个有缓存的通道来管理工作    tasks := make(chan string, taskLoad)    wg2.Add(numberGoroutines)    // 启动goroutine来处理工作    for gr := 1; gr <= numberGoroutines ; gr++  {        go worker(tasks, gr)    }    // 增加一组要完成的工作    for post := 1; post <= taskLoad; post++ {        tasks "Task : %d", post)    }    // 当所有工作都处理完时关闭通道,以便所有通道退出    // 当关闭通道后,goroutine依旧可以从通道接收数据,但是不能再发送数据    close(tasks)    // 等待所有工作完成    wg2.Wait()    }// worker作为goroutine启动来处理,从有缓存的通道传入数据func worker(tasks chan string, worker int) {    // 通知函数已经返回    defer wg2.Done()    for {        // 等待分配工作        task, ok :=         if !ok {            // 这意味着通道已经空了,并且已被关闭            fmt.Printf("Worker: %d : shutting down\n", worker)            return        }        // 显示我们开始工作了        fmt.Printf("Worker: %d : Started %s\n", worker, task)        // 随机等一段时间来完成工作        sleep := rand.Int63n(100)        time.Sleep(time.Duration(sleep) * time.Millisecond)        // 显示我们完成工作了        fmt.Printf("Worker: %d : Completed %s\n", worker, task)    }}

四通道结合 select
1、select 详解
select {case // 如果从 chan1 通道成功接收数据,则执行该分支代码case chan2 1:   // 如果成功向 chan2 通道成功发送数据,则执行该分支代码 default:   // 如果上面都没有成功,则进入 default 分支处理流程 }
  • 每个 case 语句都必须是一个面向通道的操作。

  • 多个 case 并行执行,select 选择先操作成功返回的那个 case 执行,同时返回,随机选择一个。

  • 都没返回,进入 default 分支,不会出现阻塞。

  • Chan1 通过为空或者 chan2 通道已满,立即进入 default 分支。

  • 没有 default 分支,则会阻塞直到某个通道操作成功。

借助 select 语句我们可以在一个协程中同时等待多个通道达到就绪状态。

917d9db09f11da5058ca854886c71be5.png

2、代码示例

select 简单代码示例:

package mainimport (    "fmt"    "math/rand")func main() {    // 定义三个chan int类型元素的通道数组    chs := [3]chan int{make(chan int, 1), make(chan int, 1), make(chan int, 1)}    // 随机生成0-2之间的数字    index := rand.Intn(3)    // 向通道发送随机数    chs[index]     // 哪一个通道中有值,那个对应的分支就会被执行    select {    case 0]:        fmt.Println("第一个条件分支被选中")    case 1]:        fmt.Println("第二个条件分支被选中")    case 2]:        fmt.Println("第三个条件分支被选中")    default:        fmt.Println("没有分支被选中")    }}

使用 Select+ 超时实现无阻塞读写:

package mainimport (    "time"    "fmt")// 使用Select+超时实现无阻塞读写func main() {    c1 := make(chan string, 1)    c2 := make(chan string, 1)    go func() {        time.Sleep(time.Millisecond * 100)        c1 "name : james"    }()    go func() {        time.Sleep(time.Millisecond * 100)        c2 "age : 34"    }()    for i := 0; i < 3; i++ {        // 给通道创建容忍时间,定时器功能,default立即返回        tm := time.NewTimer(time.Second * 5)        select {         case msg1 :=             fmt.Println(msg1)         case msg2 :=             fmt.Println(msg2)         case             fmt.Println("send data timeout!")        }    }}

出处:https://url.cn/5s5kzl6

文章好看点这里

eea51af11eecf782831c7743b26603c0.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值