兄弟连区块链培训教程并发技术3:管道通信

兄弟连区块链培训 · · 1677 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  很多人喜欢Go语言,其实是因为Go语言有其独特的语言属性在支撑着其在编程语言界的发展,今天兄弟连Go语言+区块链培训老师给大家介绍一下关于Go语言并发技术3:管道通信,下面我们一起来看一下吧。( http://bt.itxdl.cn/ )

channel 介绍

channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。

channel 创建

channel 使用内置的 make 函数创建,下面声明了一个 chan int 类型的 channel:

ch := make(chan int)

· 1

c和 map 类似,make 创建了一个底层数据结构的引用,当赋值或参数传递时,只是拷贝了一个 channel 引用,指向相同的 channel 对象。和其他引用类型一样,channel 的空值为 nil 。使用 == 可以对类型相同的 channel 进行比较,只有指向相同对象或同为 nil 时,才返回 true

channel 的读写操作

ch := make(chan int)

// write to channel

ch <- x

// read from channel

x <- ch

// another way to read

x = <- chnnel 一定要初始化后才能进行读写操作,否则会永久阻塞。

channel 一定要初始化后才能进行读写操作,否则会永久阻塞。

关闭 channel

golang 提供了内置的 close 函数对 channel 进行关闭操作。

ch := make(chan int)close(ch)

有关 channel 的关闭,你需要注意以下事项:

1. 关闭一个未初始化(nil) 的 channel 会产生 panic

2. 重复关闭同一个 channel 会产生 panic

3. 向一个已关闭的 channel 中发送消息会产生 panic

4. 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为

5. false 的 ok-idiom,可以用它来判断 channel 是否关闭

6. 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息

ch := make(chan int, 10)

ch <- 11

ch <- 12

close(ch)

forx := range ch {

    fmt.Println(x)

}


x, ok := <- ch

fmt.Println(x, ok)



-----

output:

11

12

0 false

channel 的类型

channel 分为不带缓存的 channel 和带缓存的 channel。

无缓存的 channel

从无缓存的 channel 中读取消息会阻塞,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息。

通过无缓存的 channel 进行通信时,接收者收到数据 happens before 发送者 goroutine 唤醒

有缓存的 channel

有缓存的 channel 的声明方式为指定 make 函数的第二个参数,该参数为 channel 缓存的容量

ch := make(chan int, 10)

· 1

有缓存的 channel 类似一个阻塞队列(采用环形数组实现)。当缓存未满时,向 channel 中发送消息时不会阻塞,当缓存满时,发送操作将被阻塞,直到有其他 goroutine 从中读取消息;相应的,当 channel 中消息不为空时,读取消息不会出现阻塞,当 channel 为空时,读取操作会造成阻塞,直到有 goroutine 向 channel 中写入消息。

ch := make(chan int, 3)

// blocked, read from empty buffered channel

<- ch

ch := make(chan int, 3)

ch <- 1

ch <- 2

ch <- 3

// blocked, send to full buffered channel

ch <- 4

通过 len 函数可以获得 chan 中的元素个数,通过 cap 函数可以得到 channel 的缓存长度。

实例

通过channel实现同步

导入依赖

import (

    "fmt"

    "time"

)


//语法点①:创建int类型的无缓存管道//var ch = make(chan int)varch = make(chan int,0)

funcPrinter(str string) {

    for_, data := range str {

        fmt.Printf("%c", data)

        time.Sleep(time.Second)

    }

    fmt.Printf("\n")

}

func person1() {

    //打印完需要7秒钟

    //劳资不打印完是不会往管道中塞数据的,阻塞不死你丫的

    Printer("今生注定我爱你")


    //箭头指向管道内部,写数据

    //在打完今生注定我爱你(耗时7秒钟)后,才写入数据

    //语法点②:向管道里写数据,无论读写,箭头只能朝左

    //语法点⑤:如果管道缓存已满,则阻塞等待至有人取出数据腾出空间,再写入

    ch <- 666

}

func person2() {

    //箭头指向管道外面,代表从管道中拿出数据,读数据


    //语法点③:从管理取出数据,但不不接收

    //语法点⑥:管道里没数据时,阻塞死等

    <-ch


    //语法点④:从管理取出数据,且使用data变量接收

    //data:=<-ch

    //fmt.Println("读出数据:",data)


    //终于妈的可以打印了

    Printer("FUCKOFF")

}

func main() {


    go person1()

    go person2()


    //主协程赖着不死

    for {

        time.Sleep(time.Second)

    }

}

通过channel实现同步和数据交互

package main

import (

    "fmt"

    "time"

)

func main() {

    //创建无缓存管道

ch := make(chan string)


    //5、主协程结束

    defer fmt.Println("主协程也结束")


    //子协程负责写数据

    go func() {

        //3、结束任务

        defer fmt.Println("子协程调用完毕")


        //1、缓缓打印2次序号

        for i := 0; i < 2; i++ {

            fmt.Println("子协程 i= ", i)

            time.Sleep(time.Second)

        }


        //2、向管道发送数据

ch <- "我是子协程,工作完毕"

    }()


    //4、阻塞接收

    str := <-ch

    fmt.Println("str = ", str)

}

无缓冲的channel

package main

import (

    "fmt"

    "time"

)

func main() {

    //创建一个无缓冲的管道

ch := make(chan int, 1)


    //长度0,缓存能力0

    fmt.Printf("len(ch) = %d, cap(ch)=%d\n", len(ch), cap(ch))


    go func() {

        //向管道中存入0,被阻塞,存入1,被阻塞,存入2

        for i := 0; i < 3; i++ {

            fmt.Println("子协程: i = ", i)

            ch <- i


            fmt.Println("5秒以内被打印出来给杰神100万!")

        }

    }()


    //睡眠2秒

    time.Sleep(5 * time.Second)


    //读取0,被阻塞,读取1,被阻塞,读取2

    for i := 0; i < 3; i++ {

        num := <-ch

        fmt.Println("num = ", num)

    }


}

有缓存的channel

package main

import (

    "fmt"

    "time"

)

func main() {

    //创建3缓存的管道

ch := make(chan int, 3)

    //长度0,缓存能力3(即使没人读,也能写入3个值)

    fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))


    //一次性存入3个:012,3456789

    go func() {

        for i := 0; i < 10; i++ {

            ch <- i

            fmt.Printf("子协程存入[%d]: len(ch) = %d, cap(ch) = %d\n", i, len(ch), cap(ch))

            //time.Sleep(1 * time.Second)

        }

    }()


    //time.Sleep(5 * time.Second)


    //一次性读取3个:012,345,678,9

    for i := 0; i < 10; i++ {

        num := <-ch

        fmt.Println("num = ", num)

    }

    time.Sleep(1*time.Nanosecond)

}

更多区块链视频教程/源码/课件/学习资料-企鹅QUN:591229276


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:兄弟连区块链培训

查看原文:兄弟连区块链培训教程并发技术3:管道通信

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1677 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传