go 基础之并发笔记

go 基础之并发笔记

协程

与传统的系统级线程和进程相比,协程的大优势在于其“轻量级”,可以轻松创建上百万个而不会导致系统资源衰竭,而线程和进程通常多也不能超过1万个。这也是协程也叫轻量级线程的原因。


go + 函数名:启动一个协程执行函数体

package main
import
(
  "fmt"
  "time"
)
func Add(x, y int) {
	z := x + y
	fmt.Println(z)
}

func main()  {
  for i := 0; i < 10; i++ {
    go Add(i,i)
  }
  time.Sleep(2 * time.Second)
}

12
8
10
4
6
16
14
0
18
2

channel

channel初始化必须要make


channel的读写

ch <- c 写  
c := <-ch 读

在读同一个channel的时候如果没有数据,则会堵塞掉 直到写数据到这个channel里

package main
import
(
  "fmt"
  "strconv"
  "time"
)

func Read(ch chan int)  {
  fmt.Println("Read channel")
  value := <- ch
  fmt.Println("value:" + strconv.Itoa(value))
}
func Write(ch chan int)  {
  fmt.Println("Write channel")
  ch <- 10
}

func main()  {
  ch := make(chan int)
  go Read(ch)
  go Write(ch)
  time.Sleep(10 * time.Second)
}
Read channel
Write channel
value:10

这种channel的阻塞特性可以用来同步协程,举个例子

func Add(x, y int, ch chan int) {
	z := x + y
	fmt.Println(z)
  ch <- 1
}

func main()  {
  chs := make([]chan int,10)
  for i := 0; i < 10; i++ {
    chs[i] = make(chan int)
    go Add(i,i,chs[i])
  }
  // time.Sleep(2 * time.Second)
  for _,v := range chs{
      <- v
    }
}

2
8
4
6
0
12
10
14
18
16

缓存channel

c:=make(chan int,n),n为缓存区的大小,c:=make(chan int,0) 等价于c:=make(chan int)


写channel的时候如果超过它的缓存区并且没有被读取的话,写操作将堵塞掉

var ch chan int
func test_channel()  {
  ch <- 1
  fmt.Println("ch 1")
  ch <- 1
  fmt.Println("ch 2")
  ch <- 1
  fmt.Println("come to end goroutine 1")
}

func main()  {
  ch = make(chan int,2)
  go test_channel()
  time.Sleep(2 * time.Second)
  fmt.Println("running end!")
  <- ch
  time.Sleep(time.Second)
}
ch 1
ch 2
running end!
come to end goroutine 1

select

select{
  case <- chan1: // 如果chan1成功读到数据,则进行该case处理语句
  case chan2 <- 1:  // 如果成功向chan2写入数据,则进行该case处理语句
  default:  // 如果上面都没有成功,则进入default处理流程
}

超时的经典控制实现

func main()  {
  ch := make(chan int)
  timeout := make(chan int)

  go func ()  {
    time.Sleep(time.Second)
    timeout <- 1
  }()

  select{
  case <- ch:
    fmt.Println("come to ch")
  case <- timeout:
    fmt.Println("come to timeout")
  }
}
come to timeout

利用time包的After方法实现超时控制

import
(
  "fmt"
  "time"
)
func main()  {
  ch := make(chan int)

  select{
  case <- ch:
    fmt.Println("come to ch")
  case <- time.After(time.Second):
    fmt.Println("come to timeout")

come to timeout

协程与线程的区别

一般协程不会去出让cpu的使用权,不像线程是根据cpu的调度算法来切换使用的
协程切换cpu的使用主要通过以下两种方式来实现的

  1. 该任务的业务代码主动要求切换,即主动让出执行权
(
  "fmt"
  "runtime"
  "strconv"
  "time"
)
func main()  {
  // 协程1
  go func ()  {
    for i := 0; i < 10; i++ {
      if i == 5 {
        runtime.Gosched() // 主动要求出让cpu
      }
      fmt.Println("routine 1 "+ strconv.Itoa(i))
    }
  }()

  //  协程2
  go func ()  {
    for i := 10; i < 20; i++ {
      fmt.Println("routine 2 "+ strconv.Itoa(i))
    }
  }()

  time.Sleep(time.Second)
}
routine 1 0
routine 1 1
routine 1 2
routine 1 3
routine 1 4
routine 2 10
routine 2 11
routine 2 12
routine 2 13
routine 2 14
routine 2 15
routine 2 16
routine 2 17
routine 2 18
routine 2 19
routine 1 5
routine 1 6
routine 1 7
routine 1 8
routine 1 9
  1. 发生了IO,导致执行阻塞
func main()  {
  ch := make(chan int)
  // 协程1
  go func ()  {
    for i := 0; i < 10; i++ {
      if i == 5 {
        <- ch
      }
      fmt.Println("routine 1 "+ strconv.Itoa(i))
    }
  }()

  //  协程2
  go func ()  {
    for i := 10; i < 20; i++ {
      fmt.Println("routine 2 "+ strconv.Itoa(i))
    }
    ch <- 1
  }()

  time.Sleep(time.Second)
}
routine 1 1
routine 1 2
routine 1 3
routine 1 4
routine 2 10
routine 2 11
routine 2 12
routine 2 13
routine 2 14
routine 2 15
routine 2 16
routine 2 17
routine 2 18
routine 2 19
routine 1 5
routine 1 6
routine 1 7
routine 1 8
routine 1 9

转载于:https://my.oschina.net/u/3628952/blog/1840412

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值