美文网首页
go并发编程 - 待完善

go并发编程 - 待完善

作者: 如逆水行舟不进则退 | 来源:发表于2020-08-25 10:14 被阅读0次

package main

import (
  "fmt"
  "time"
)

func main() {
  //fmt.Println("Hello World")
  go printNum()
  go printLetter()
  time.Sleep(3 * time.Second)
  fmt.Println("\n main over ... ")
}

func printNum() {
  for i:=1;i<=5;i++ {
    time.Sleep(250 * time.Millisecond)
    fmt.Printf("%d", i)
  }
}

func printLetter() {
  for i:='a';i<='e';i++ {
    time.Sleep(400 * time.Millisecond)
    fmt.Printf("%c", i)
  }
}
  • 定义channel
package main

func main() {
  //fmt.Println("Hello World")
  var ch0 chan string
  ch1 := make(chan string)
  ch2 := make(chan interface{})
  type Equip struct {}
  ch3 := make(chan *Equip)
}

  • 循环接收channel 数据
package main

import "fmt"

func main() {
  //fmt.Println("Hello World")
  ch1 := make(chan string)
  go sendData(ch1)
  // 1.循环接受数据方式1
  //for {
  //  data := <-ch1
  //  if data == "" {
  //    break
  //  }
  //  fmt.Println("从通道数据中读取数据方式1:", data)
  //}

  // 2.循环接受数据方式2
  //for {
  //  data, ok := <- ch1
  //  fmt.Println(ok)
  //  if !ok {
  //    break
  //  }
  //  fmt.Println("从通道数据中读取数据方式2:", data)
  //}

  // 3.循环接收数据方式3
  // for ... range 循环会自动判断通道是否关闭,自动break循环
  for value := range ch1 {
    fmt.Println("从通道数据中读取数据方式3:", value)
  }

}

func sendData(ch1 chan string) {
  defer close(ch1)
  for i := 0; i < 3; i++ {
    ch1 <- fmt.Sprintf("发送数据 %d\n", i)
  }
  fmt.Println("发送数据完毕。。")
  // 显式调用close() 实现关闭通道
}

  • channel 阻塞
package main

import (
  "fmt"
  "time"
)

func main() {
  //fmt.Println("Hello World")
  var ch1 chan int
  ch1 = make(chan int)
  fmt.Printf("%T \n", ch1)
  ch2 := make(chan bool)
  go func() {
    data, ok := <-ch1
    if ok {
      fmt.Println("子goroutine 取到数值:", data)
    }
    time.Sleep(1 * time.Second)
    fmt.Println("子goroutine over ...")
    ch2 <- true
  }()
  ch1 <- 10
  <-ch2 // 阻塞
  fmt.Println("main over ...")
}


  • 关闭channel
package main

import "fmt"

func main() {
  //fmt.Println("Hello World")
  var ch1 chan int
  ch1 = make(chan int)
  go func() {
    ch1 <- 100
    ch1 <- 100
    close(ch1)
    //ch1 <- 10 // 关闭channel, 无法写入数据
  }()
  data, ok := <-ch1
  fmt.Println("main 读取数据:", data, ok)
  data, ok = <-ch1
  fmt.Println("main 读取数据:", data, ok)
  data, ok = <-ch1
  fmt.Println("main 读取数据:", data, ok)
  data, ok = <-ch1
  fmt.Println("main 读取数据:", data, ok)
  data, ok = <-ch1
  fmt.Println("main 读取数据:", data, ok)
}

输出:
main 读取数据: 100 true
main 读取数据: 100 true
main 读取数据: 0 false
main 读取数据: 0 false
main 读取数据: 0 false

  • 缓冲channel
package main

import (
  "fmt"
  "time"
)

func main() {
  //fmt.Println("Hello World")
  //1.非缓冲通道
  ch1 := make(chan int)
  fmt.Println("非缓冲通道", len(ch1), cap(ch1))
  go func() {
   data := <- ch1
   fmt.Println("获得数据", data)
  }()
  ch1 <- 100
  time.Sleep(time.Second)
  fmt.Println("赋值ok","main over...")

  //2.非缓冲通道
  ch2 := make(chan string)
  go sendData(ch2)
  time.Sleep(time.Second)
  for data := range ch2 {
    time.Sleep(time.Second)
    fmt.Println("\t 读取数据:", data)
  }
  fmt.Println("main over ...")

  // 缓冲通道,缓冲区满了才会阻塞
  ch3 := make(chan string, 6)
  go sendData(ch3)
  for data := range ch3 {
   fmt.Println("\t 读取数据:", data)
  }
  fmt.Println("main over")
}

func sendData(ch chan string) {
  for i := 1; i <=3; i++ {
    ch <- fmt.Sprintf("data %d", i)
    fmt.Println("往通道放入数据:", i)
  }
  defer close(ch)
}

package main

import (
  "fmt"
  "math/rand"
  "strings"
  "time"
)

func main() {
  //fmt.Println("Hello World")
  // 用channel来传递数据,不再需要自己去加锁维护一个全局的阻塞队列
  ch1 := make(chan int)
  ch_bool1 := make(chan bool) // 判断结束
  ch_bool2 := make(chan bool) // 判断结束
  ch_bool3 := make(chan bool) // 判断结束

  rand.Seed(time.Now().UnixNano())

  // 生产者
  go producer(ch1)
  // 消费者
  go consumer(1, ch1, ch_bool1)
  go consumer(2, ch1, ch_bool2)
  go consumer(3, ch1, ch_bool3)

  <-ch_bool1
  <-ch_bool2
  <-ch_bool3

  defer fmt.Println("main...over...")

  fmt.Println("main over")
}

func producer(ch1 chan int) {
  for i := 1; i <= 10; i++ {
    ch1 <- i
    fmt.Println("生产蛋糕,编号:", i)
    time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
  }
  defer close(ch1)
}

func consumer(num int, ch1 chan int, ch chan bool) {
  for data := range ch1 {
    pre := strings.Repeat("——————", num)
    fmt.Printf("%s %d 号购买 %d号蛋糕 \n", pre, num, data)
    time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
  }
  ch <- true
  defer close(ch)
}



  • 单向channel
package main

import (
  "fmt"
  "time"
)

func main() {
  // 双向通道
  ch1 := make(chan string)
  go fun1(ch1)

  data := <-ch1

  fmt.Println("main,接受到数据:", data)
  ch1 <- "Go语言好学吗?"
  ch1 <- "Go语言好学吗???"
  go fun2(ch1)
  go fun3(ch1)
  time.Sleep(time.Second)

  fmt.Println("main over")
}

func fun1(ch1 chan string) {
  ch1 <- "我是Steven老师"
  data := <-ch1
  data2 := <-ch1
  fmt.Println("回应:", data, data2)
}

// 功能:只有写入数据
func fun2(ch1 chan<- string) {
  // 只能写入
  ch1 <- "How are you?"
}

func fun3(ch1 <-chan string) {
  data := <-ch1
  fmt.Println("只读:", data)
}


  • NewTimer()函数
package main

import (
  "fmt"
  "time"
)

func main() {

  // 创建计时器
  timer1 := time.NewTimer(5 * time.Second)
  // fmt.Printf("%T \n", timer1)
  fmt.Println(time.Now())
  data := <- timer1.C // <-chan time.Time
  fmt.Printf("%T \n", timer1.C)
  fmt.Printf("%T \n", data)
  fmt.Println(data)
}
  • After()函数
package main

import (
  "fmt"
  "time"
)

func main() {

  // 2.使用After(), 返回值 <-chan Time , 同Timer.C
  ch1 := time.After(5 * time.Second)
  fmt.Println(time.Now())
  data := <-ch1
  fmt.Printf("%T \n", data)
  fmt.Println(data)
}
  • select 随机挑选case
package main

import (
  "fmt"
)

func main() {

  ch1 := make(chan int)
  ch2 := make(chan int)
  go func() {
    ch1 <- 100
  }()

  go func() {
    ch2 <- 200
  }()

  // time.Sleep(time.Second)
  select {
  case data := <-ch1:
      fmt.Println("ch1中读取数据了:", data)
  case data := <-ch2:
      fmt.Println("ch2中读取数据了:", data)
  default:
      fmt.Println("执行了default...")
  }
}


  • 观察select的阻塞机制
package main

import (
  "fmt"
  "time"
)

func main() {

  ch1 := make(chan int)
  ch2 := make(chan int)

  go func() {
    time.Sleep(10 * time.Millisecond)
    data := <-ch1
    fmt.Println("ch1:", data)
  }()

  go func() {
    time.Sleep(2 * time.Second)
    data := <-ch2
    fmt.Println("ch2:", data)
  }()

  select {
  case ch1 <- 100 :// 阻塞
      close(ch1)
      fmt.Println("ch1 中写入数据")
  case ch2 <- 200: // 阻塞
      close(ch2)
      fmt.Println("ch2 中写入数据")
  case <-time.After(2 * time.Millisecond) : // 阻塞
      fmt.Println("执行延时通道")
  //default:
  //  fmt.Println("default...")
  }

  time.Sleep( 4 * time.Second)
  fmt.Printf("main over")
}


  • 同步等待组
package main

import (
  "fmt"
  "math/rand"
  "strings"
  "sync"
  "time"
)

func main() {
  var wg sync.WaitGroup
  fmt.Printf("%T \n", wg) // sync.WaitGroup
  fmt.Println(wg)
  wg.Add(3)
  rand.Seed(time.Now().UnixNano())

  go printNum(&wg, 1)
  go printNum(&wg, 2)
  go printNum(&wg, 3)

  wg.Wait() // 进入阻塞状态,当计数为0时解除阻塞
  defer fmt.Println("main over ...")

}

func printNum(wg *sync.WaitGroup, num int) {
  for i := 1; i <=3; i++ {
    // 在每个Goroutine 前面添加多个制表符方便观看打印结果
    pre := strings.Repeat("\t", num-1)
    fmt.Printf("%s 第 %d号子goroutine, %d \n", pre, num, i)
    time.Sleep(time.Second)
  }
  wg.Done() // 计数器减1
}

  • 互斥锁
package main

import (
  "fmt"
  "strconv"
  "strings"
  "sync"
  "time"
)
var tickets int = 20
var wg sync.WaitGroup
var mutex sync.Mutex

func main() {
  wg.Add(4)
  go saleTickets("1号窗口", &wg)
  go saleTickets("2号窗口", &wg)
  go saleTickets("3号窗口", &wg)
  go saleTickets("4号窗口", &wg)
  wg.Wait()
  defer fmt.Println("所有车票都售空")
}

func saleTickets(name string, wg *sync.WaitGroup) {
  defer wg.Done()
  for {
    // 锁定
    mutex.Lock()
    if tickets > 0 {
      time.Sleep(1 * time.Second)
      // 获取窗口的编号
      num, _ := strconv.Atoi(name[:1])
      pre := strings.Repeat("------", num)
      fmt.Println(pre, name, tickets)
      tickets--
    } else {
      fmt.Printf("%s 结束售票 \n", name)
      mutex.Unlock()
      break
    }
    // 解锁
    mutex.Unlock()
  }
}


相关文章

  • go并发编程 - 待完善

    定义channel 循环接收channel 数据 channel 阻塞 关闭channel 缓冲channel 单...

  • Go基础语法(九)

    Go语言并发 Go 是并发式语言,而不是并行式语言。 并发是指立即处理多个任务的能力。 Go 编程语言原生支持并发...

  • Go并发

    Go语言中的并发编程 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很...

  • 瞅一眼就会使用GO的并发编程分享

    [TOC] GO的并发编程分享 之前我们分享了网络编程,今天我们来看看GO的并发编程分享,我们先来看看他是个啥 啥...

  • day08-go.GPM

    当别人到go为什么支持高并发,或者问为什么go本身对并发编程友好?以及go与Java对比的并发对比 正确回答: 在...

  • Go语言简介

    Go语言简介 Go语言设计的初衷 针对其他语言的痛点进行设计并加入并发编程为大数据,微服务,并发而生的通用编程语言...

  • Go并发编程实战

    第一章 go语言 语言特性开放源代码静态类型和编译型跨平台自动垃圾回收原生的并发编程完善的构建工具多编程范式代码风...

  • 13 Go并发编程初探

    一、Go并发编程概述 Go以并发性能强大著称,在在语言级别就原生支持,号称能实现百万级并发,并以此独步江湖,本专题...

  • Go 基础

    基础 [TOC] 特性 Go 并发编程采用CSP模型不需要锁,不需要callback并发编程 vs 并行计算 安装...

  • go 并发编程

    在资源有限的情况下,如何最大化的利用有限的资源就是并发,提高并发 goroutine runtime包 chann...

网友评论

      本文标题:go并发编程 - 待完善

      本文链接:https://www.haomeiwen.com/subject/isnnjktx.html