并发编程

goroutine

它是go并发设计的核心。
goroutine就是协程,它比线程更小,十几个goroutine在底层可能就是五六个线程。
go语言内部实现了goroutine的内存共享,执行goroutine只需极少的栈内存(大概是4~5KB)。
main函数是主协程,如果主协程退出其他任务也不会执行,程序直接退出。

runtime包

runtime.Gosched()

runtime.Gosched()用于让出CPU时间片,调度器会重新进行任务调度,但是有可能还是分配到该任务

 package main

  import (
     "fmt"
     "runtime"
  )

  func main() {
     //匿名子协程
     go func(s string) {
        for i := 0; i < 2; i++ {
           fmt.Println(s)
        }
     }("world")
     //主协程
     for i := 0; i < 2; i++ {
        runtime.Gosched()
        fmt.Println("hello")
     }
  }
runtime.Goexit()

立即终止当前协程,但是会保证已经注册的defer延迟调用都被执行。

  package main

  import (
     "fmt"
     "time"
     "runtime"
  )

  func main() {
     //匿名子协程
     go func() {
        defer fmt.Println("A.defer")
        //匿名函数
        func() {
           defer fmt.Println("B.defer")
           //此时只有defer执行
           runtime.Goexit()
           fmt.Println("B")
        }()
        fmt.Println("A")
     }()
     for {
        time.Sleep(time.Second)
     }
  }

该程序执行只会输出

B.defer
A.defer
runtime.GOMAXPROCS()

设置并发执行的CPU个数并返回之前的值

  package main

  import (
     "runtime"
     "fmt"
  )

  func main() {
     n := runtime.GOMAXPROCS(3)
     fmt.Println("n=%d\n",n)
     //循环执行2个
     for{
        go fmt.Print(0)
        fmt.Print(1)
     }
  }
runtime. NumCPU()

返回系统的CPU数量

runtime. NumGoroutine()

返回正在执行和排队的所有任务总数

runtime.GOOS

返回操作系统

channel

创建

channel可以用内置make()函数创建
make(chan 类型) //无缓冲的通道,阻塞
make(chan 类型, 容量) //有缓冲的通道,非阻塞

传输数据
  channel <- value   //发送value到channel
  <-channel          //接收通道数据,并丢弃
  x := <-channel    //通道取值并赋给x
  x, ok := <-channel //ok是检查通道是否关闭或者是否为空
遍历
                for foundOre := range c {
            time.Sleep(2 * time.Second)
            fmt.Println("Miner: Received " + strconv.Itoa(foundOre) + " from finder")
        }

遍历会阻塞直到有新数据发送到chanel,并且在遍历成功后会继续遍历

关闭
close(c)

select

go语言提供了select关键字,可以监听channel上的数据流动
语法与switch类似,区别是select要求每个case语句里必须是一个IO操作

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

sync包

Sync包同步提供基本的同步原语。
golang 并不推荐这个包中的大多数并发控制方法,但还是提供了相关方法,主要原因是golang中提倡以共享内存的方式来通信。

sync.Mutex

互斥锁

var l sync.Mutex
l.Lock()
l.Unlock
sync.RWMutex

读写锁
同时只能有一个 goroutine 能够获得写锁定。
同时可以有任意多个 gorouinte 获得读锁定。
同时只能存在写锁定或读锁定(读和写互斥)。

var l sync.RWMutex
l. RLock() // 加读锁
l. RUnlock() //加写锁
l. Lock()//加写锁
l. Unlock()//解写锁
sync. WaitGroup

WaitGroup可以用来等待一组goroutine结束

func (wg *WaitGroup) Add(delta int)//增加delta个,注意,wg.Add() 方法一定要在 goroutine 开始前执行
func (wg *WaitGroup) Done() //计数-1
func (wg *WaitGroup) Wait() //等待结束
sync.Cond

条件变量

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)
cond. Broadcast() // 唤醒所有挂起的goroutine
cond.Signal() //唤醒1个goroutine
cond.Wait() //必须要获取锁才能调用,Wait方法在调用时会释放底层锁Locker,并且将当前goroutine挂起,直到另一个goroutine执行Signal或者Broadcase,该goroutine才有机会重新唤醒,并尝试获取Locker,完成后续逻辑
sync.Once

保证只执行一次

    var once sync.Once
    onceBody := func() {
        fmt.Println("Only once")
    }
    done := make(chan bool)
    for i := 0; i < 10; i++ {
        go func() {
            once.Do(onceBody)
            done <- true
        }()
    }
    for i := 0; i < 10; i++ {
        <-done
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容