Go语言学习笔记 --- concurrency、channel、select

来源:互联网 发布:nginx代理静态资源 编辑:程序博客网 时间:2024/05/16 14:01

学习笔记根据 无闻 go语言基础教程 整理

concurrency

  • 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级”线程池”。
  • 不过话说回来,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。
  • 另外,goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利。
  • 并发不是并行:Concurrency Is Not Parallelism
  • 并发主要由切换时间片来实现“同时”运行,在并行则是直接利用
  • 多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的能力。
  • Goroutine 奉行通过通信来共享内存,而不是共享内存来通信。

channel

  • Channel 是 goroutine 沟通的桥梁,大都是阻塞同步的
  • 通过 make 创建,close 关闭
  • Channel 是引用类型
  • 可以使用 for range 来迭代不断操作 channel
  • 可以设置单向或双向通道
  • 可以设置缓存大小,在未被填满前不会发生阻塞

select

  • 可处理一个或多个 channel 的发送与接收
  • 同时有多个可用的 channel时按随机顺序处理
  • 可用空的 select 来阻塞 main 函数
  • 可设置超时

示例代码

package mainimport (    "fmt"    "time"    "runtime"    "sync")func main() {    // 说明,由于存在异步和阻塞等情况,测试时候需要一个函数一个函数的执行    // 执行一个函数时,注释掉其他测试函数    test1T()    // test2()    // test3()    // test4()    // test5()    // test6()    // test7T()    // test8T()    // test9T()    // test10T()    // test11T()    // test12()    // test13()    // test14()}func test1() {    fmt.Println("Go Go Go !!!")}func test1T() {    fmt.Println("=============test1T=============")    go test1() // 先启动goroutine    time.Sleep(2 * time.Second) // 程序等待2s后退出}// 使用channel 来进行消息的发送func test2() {    fmt.Println("=============test2=============")    c := make(chan bool) // 创建一个channel    // 下面是用匿名函数    go func() {        fmt.Println("Go2") // Go2        c <- true // c 存进一个bool型    }()    <-c // 在外面取出来 函数在执行到这一步的时候是阻塞状态,等c 存完之后才会执行这里,结束运行}// 可以使用 for range 来迭代不断操作 channel,等待channel完成,利用close函数,最后退出程序func test3() {    fmt.Println("=============test3=============")    c := make(chan bool) // 创建一个channel ,make 即能存,又能取 (属于双向通道)    // 下面是用匿名函数    go func() {        fmt.Println("Go2")        c <- true // c 存进一个bool型        close(c) // 在迭代时一定要注意close,否则会死锁,最终崩溃退出。    }()    for v := range c {        fmt.Println(v) // true    }}// 测试 使用无缓存的channelfunc test4() {    fmt.Println("=============test4=============")    c := make(chan bool)    go func() {        fmt.Println("Go3")        c <- true    }()    <- c // main 函数先执行,再去执行 goroutine, 此处是一直等待状态, 直到取出东西来,main函数退出}// 测试 使用无缓存的channel, 尝试位置调换, 无缓存是同步阻塞状态,直到读取到,才会结束func test5() {    fmt.Println("=============test5=============")    c := make(chan bool)    go func() {        fmt.Println("Go3") // Go3        <- c    }()    c <- true}// 测试使用有缓存的channel, 并且对调位置 ,有缓存是异步的func test6() {    fmt.Println("=============test6=============")    c := make(chan bool, 1)    go func() {        fmt.Println("Go3") // 未输出        <- c    }()     c <- true // 直接退出}func test7() {    a := 1    for i := 0; i<10000000; i++ {        a += i    }    fmt.Println(a)}// 循环10个 goroutine ,什么都没输出func test7T() {    fmt.Println("=============test7T=============")    for i := 0; i < 10; i++ {        go test7()    }}func test8(c chan bool, index int) {    a := 1    for i := 0; i<100; i++ {        a += i    }    fmt.Println(index, a)    if index == 9 {        c <- true    }}// 循环输出10个 goroutine , 但结果并未按照想象中的,10个分别输出执行,可能是go的api版本升级导致的, 默认使用多核处理// 意想不到的结果是 每个goroutine 都是独立的,// 如果第9个先输出,那么其他的可能并不会输出而是直接结束func test8T() {    fmt.Println("=============test8T=============")    c := make(chan bool)    for i := 0; i < 10; i++ {        go test8(c, i)    }    <-c}// 循环10个 goroutine , 但是每个goroutine 都是独立的,如果第9个先输出,那么其他的可能并不会输出而是直接结束// 同样使用test8 , 现在的情况是,输出结果和test8T是一样的func test9T() {    fmt.Println("=============test9T=============")    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定    c := make(chan bool)    for i := 0; i < 10; i++ {        go test8(c, i)    }    <-c}func test10(c chan bool, index int) {    a := 1    for i := 0; i<100; i++ {        a += i    }    fmt.Println(index, a)    c <- true}// 设置一个缓存长度为10的channel, 使用此种方法,10个channel会分别输出执行func test10T() {    fmt.Println("=============test10T=============")    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定    c := make(chan bool, 10) // 此处channel 长度为10    for i := 0; i < 10; i++ {        go test10(c, i)    }    // 循环取10次    for i := 0; i < 10; i ++ {        <-c    }}// 使用第二种方法来完成执行10个channel// 通过sync 同步包中的waitgroup 来创建一个任务组,在任务组中添加要完成的任务数// 在没完成一次任务,就标记一次档,待完成任务数就会往下减,直到为0时,全部任务就会完成。func test11(wg *sync.WaitGroup, index int) {    a := 1    for i := 0; i < 100; i++ {        a += i    }    fmt.Println(index, a)    wg.Done()}func test11T() {    fmt.Println("=============test11T=============")    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定    wg := sync.WaitGroup{}    wg.Add(10)    for i := 0; i < 10; i++ {        go test11(&wg, i)    }    wg.Wait()}// 使用select 测试多个channelfunc test12() {    fmt.Println("=============test12=============")    c1, c2 := make(chan int), make(chan string)    o := make(chan bool, 2) // 设置一个缓存为2的channel    go func() {        for {            select {            case v, ok := <-c1:                if !ok {                    o <- true                    break                }                fmt.Println("c1",v)            case v, ok := <- c2:                if !ok {                    o <- true                    break                }                fmt.Println("c2", v)            }        }    }()    c1 <- 1    c2 <- "hi"    c1 <- 3    c2 <- "hello"    close(c1)    close(c2)    for i := 0; i < 2; i ++ {        <- o    }    /*    // 程序运行结果:    c1 1    c2 hi    c1 3    c2 hello     */}func test13() {    fmt.Println("=============test13=============")    c := make(chan int)    go func() {        // 不断读出c的值        for v := range c {            fmt.Println(v)        }    }()    // 随机向c中写入0或者1    for i := 0; i < 6; i++ {        select {        case c <- 0:        case c <- 1:        }    }    /*    // 随机输出结果:    1    1    0    0    0     */}// select 可设置超时func test14() {    fmt.Println("=============test14=============")    c := make(chan bool)    select {    case v:= <-c: // 此处未向c中放入数据,所以程序一直在等待        fmt.Println(v)    case <- time.After(3 * time.Second): // time.After 返回一个channel,case被执行,输出 Timeout After 3 second        fmt.Println("Timeout After 3 second")    }    /*    // 程序在3s后的运行结果:    Timeout After 3 second     */}
原创粉丝点击