Golang channel 的基本使用方法

package main

import (
    "fmt"
    "learner/Add"
    "time"
)

//a. 普通类型,普通变量保存的就是值,也叫值类型
//b. 获取普通变量的内存地址,用&,比如: var a int, 获取a的内存地址:&a
//c. 指针类型,指针变量存的就是一个内存地址,这个地址指向值
//d. 获取指针类型所指向的值,使用:*,比如:var *p int, 使用*p获取p指向的值
//e. 将一个内存地址给一个指针类型进行赋值(不可以直接将变量赋值给指针,需要将内存地址赋值给指针): var a int=5, var p *int = &a

// 在工程上有两种最常见的并发通信模型: 共享数据和消息通信, go语言选择后者,通过通信进行共享内存
// channel, goroutine 间的通信方式, 进程内的的通信.进程间的通信建议使用socket或者http等通信协议.
// channel 是类型相关的, 一个channel只能传递一种指定类型的值, 这个值需要在声明channel时指定(可以理解为指定元素类型的管道)

// 超时控制的经典实现
func chan_time_out_handler(ch chan int)  (item bool){
    // 使用 select 为channel实现超时机制, select的一个case必须是一个面向channel的操作
    // 定义一个time_out chan
    timeOut := make(chan bool, 1)
    go func(){
        time.Sleep(1e9) // 等待一秒钟
        timeOut<- true
    }()
    // 利用time_out这个chan实现超时之后做何操作
    select {
        case a := <- ch: // 尝试从ch这个chan中读取数据
            fmt.Println(a)
            return true
        case <- timeOut: // 在等待时间范围内一直没有从ch中读取到了数据但是从time_out 这个 chan 中读取到了数据
            return false
    }
}

// 只往chan中写入数据
func chan_in(ch_in chan<- int)  {
    for i:=0; i <= 10; i++{
        ch_in <- 1
    }
    // 如果是使用range遍历chan, 那么当chan关闭时, 读取操作会立即结束,跳出循环(注意,这是channel中可能仍会存在数据),
    // channel关闭后,其实仍然可以从中读取已发送的数据(使用range无法实现, 可以使用常规的循环读取channel的方式),读取完数据后,将读取到零值,可以多次读取(仍然是零值)
    close(ch_in) // 当多个goroutine都使用了同一个channel时, 任何一个goroutine 中关闭了这个了这个channel, 其他goroutine将无法继续对这个channel进行读取
}

// 只从chan读出数据
func chan_out(ch_out <-chan int)  {
    // 使用range, 当这个channel关闭时,就会跳出循环,但是channel里面仍然可能存在数据
    // x, ok := <- ch_out, 如果ok返回的是false,那么就表示这个chan已经关闭
    for value := range ch_out{
        fmt.Printf("+++++++++++++++++%d", value)
    }
}

func main() {

    // 切片和map 都是指针类型的数据, 指针类型的数据都可以使用make()进行分配内存
    chs := make([]chan int, 10) // 定义一个切片并分配内存, 元素是chan类型, 这个chan内可以保存的元素为int类型, 该切片初始内存可以保存10个元素(不是channel的缓冲区, 是切片的初始大小)
    for i := 0; i < 10; i++ {
        chs[i] = make(chan int, 3) // 定义一个chan并分配内存, 缓冲区大小为3, 然后保存到切片中, 如果不设置缓冲区,当写入一个元素时,如果这个元素不被读取掉,写操作将会被阻塞
        go Add.TestAddTwo(chs[i])  // 开启协程发送chan
    }

    for _, ch := range (chs) {
        fmt.Println("====================", len(ch)) //当程序运行到这里时, 这个channel有可能并没有写入数据,所以长度有可能为0 1 2
        a := <-ch  // 当这里从当前channel读取不到数据时就会阻塞
        // b := <-ch  // 继续读取, 读取不到就堵塞
        fmt.Println(a)

        item := chan_time_out_handler(ch)
        fmt.Println(item)

        // 当channel写完数据操作完成后如果没有关闭,读取完数据,chan为空时,将会阻塞, 从而有可能造成死锁, 所以chan使用完必须关闭

    }

    // 单向channel的实现,当需要将一个单向的channel从读变为写,或者从写变为读时,需要进行类型转换
    // 第一个步,定义一个正常的channel
    ch_normal := make(chan int)
    // 第二步进行类型转换,将ch_normal 转换为只允许写的channel
    var ch_in chan<- int = ch_normal
    go chan_in(ch_in)
    // 第三步 生成一个只允许进行读的channel
    var ch_out  <-chan int = ch_normal
    chan_out(ch_out)
}

// 当向一个channel写入数据, 在这个channel被读取前, 这个操作是阻塞的(在缓冲区写满之前, 即使没有读取操作,写操作都不会阻塞)
// 当从一个channel读取数据时,在对应的channel写入数据前, 这个操作也是阻塞的,从而可以利用channel实现了类似锁的功能, 进而保证
// 了所有goroutine完成后主函数才返回
// 缓冲区满之后将会阻塞,除非有goroutine对其进行操作, 否则协程就会停留在向该channel写入元素的步骤上, 直到主进程退出, 向channel写入数据的协程也就退出. 协程的阻塞不影响主进程的执行

// 定义一个channel var chanName chan ElementType
// 多层定义,例如定义一个 map, 键是string类型,元素是bool类型的channel:  var myMap map[string] chan bool
// 声明以后,定义一个channel 并赋值给变量: map["firstChan"] := make(chan false) , 使用内建函数make()

// 如果是使用range遍历chan, 那么当chan关闭时, 读取操作会立即结束,跳出循环(注意,这是channel中可能仍会存在数据)
// 当多个goroutine都使用了同一个channel时, 任何一个goroutine 中关闭了这个了这个channel,
// 其他goroutine将无法继续对这个channel进行读取, 可以在主进程中进行守护, 等所有的goroutine执行完毕后再去关闭channel
// close(chan) //关闭一个channel

// 判断一个channel是否已关闭
//1. 如果channel已经关闭,继续往它发送数据会导致panic: send on closed channel
//2. 关闭一个已经关闭的channel也会导致panic: close of closed channel
func test2(ch chan int){
    for{
        if value,ok:=<-ch;ok{
            //do somthing
            fmt.Print(value)
        }else{
            break //ok 为false, 表示channel已经被关闭,退出循环
        }
    }
}

// channel关闭后,仍然可以从中读取已发送的数据(使用range无法实现),读取完数据后,将读取到零值,可以多次读取。
func test1(){
    ch:=make(chan int,3)
    ch<-3
    ch<-2
    ch<-1
    close(ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
}

原文地址:https://www.cnblogs.com/lowmanisbusy/p/10090804.html

时间: 2024-10-09 05:21:58

Golang channel 的基本使用方法的相关文章

golang导出csv乱码解决方法

golang导出csv乱码解决方法: 在csv文件的开头写入 UTF-8 BOM // 创建文件 dstf, err := os.Create(``"./data/" + fileName) defer dstf.Close() if err != nil { beego.Error(``"create file error: " + err.Error()) return ""``, err } `dstf.WriteString(``&quo

Golang Channel用法简编

转自:http://tonybai.com/2014/09/29/a-channel-compendium-for-golang/ 在进入正式内容前,我这里先顺便转发一则消息,那就是Golang 1.3.2已经正式发布了.国内的golangtc已经镜像了golang.org的安装包下载页面,国内go程序员与爱好者们可以到"Golang中 国",即golangtc.com去下载go 1.3.2版本. Go这门语言也许你还不甚了解,甚至是完全不知道,这也有情可原,毕竟Go在TIOBE编程语

golang channel 用法

一.Golang并发基础理论 Golang在并发设计方面参考了C.A.R Hoare的CSP,即Communicating Sequential Processes并发模型理论.但就像John Graham-Cumming所说的那样,多数Golang程序员或爱好者仅仅停留在“知道”这一层次,理解CSP理论的并不多,毕竟多数程序员是搞工程 的.不过要想系统学习CSP的人可以从这里下载到CSP论文的最新版本. 维基百科中概要罗列了CSP模型与另外一种并发模型Actor模型的区别: Actor模型广义

golang channel本质——共享内存

channel是golang中很重要的概念,配合goroutine是golang能够方便实现并发编程的关键.channel其实就是传统语言的阻塞消息队列,可以用来做不同goroutine之间的消息传递,由于goroutine是轻量级的线程能够在语言层面调度,所以channel在golang中也常被用来同步goroutine. 一般channel的声明形式为:var chanName chan ElementType ElementType指定这个channel所能传递的元素类型. 定义一个cha

golang channel 使用总结

原文地址 不同于传统的多线程并发模型使用共享内存来实现线程间通信的方式,golang 的哲学是通过 channel 进行协程(goroutine)之间的通信来实现数据共享: Do not communicate by sharing memory; instead, share memory by communicating. 这种方式的优点是通过提供原子的通信原语,避免了竞态情形(race condition)下复杂的锁机制.channel 可以看成一个 FIFO 队列,对 FIFO 队列的读

golang学习之旅:方法、函数使用心得

假设要在$GOPATH/pkg/$GOOS_$GOARCH/basepath/ProjectName/目录下开发一个名为xxx的package.(这里basepath指的是github.com/michael-lau) 步骤如下: 在$GOPATH/pkg/$GOOS_$GOARCH/basepath/ProjectName/目录下新建一个目录,命名为A(目录的名字可以任取,和包名没有关系,只要最终客户端程序import的时候保持和这个目录一致即可.不过推荐和包名一致). 然后在A这个目录下编写

【GoLang】GoLang 单元测试、性能测试使用方法

单元测试代码: ackage test import ( // "fmt" "testing" ) func Test_FlowControl(t *testing.T) { var x int64 = 10 if x == 10 { // fmt.Println("x is 10") t.Log("x is 10") } else { // fmt.Println("x is not 10") t.Log

golang channel select

尝试多个channel同时触发时,select的表现: package main import ( "fmt" "time" ) func loop(ch chan int){ for i := 0; i < 10; i++ { ch <- i } close(ch) } func main() { ch1 := make(chan int, 100) ch2 := make(chan int, 100) go loop(ch1) go loop(ch2

GOLANG 加密,解密,GUID 小方法

golang的 MD5加密.BASE64解密  guid 的代码: /** * 用于加密,解密,(包含MD5加密和base64加密/解密)以及GUID的生成 * 时间: * zhifieya */ package safe import ( "crypto/md5" "crypto/rand" "crypto/sha1" "encoding/base64" "encoding/hex" "io&q