公告

微信

欢迎大家私信交流

Skip to content

并发编程

什么是并发

串行:一条流水线做一件事

并行:多条流水线做多件事

并发:一条流水线做多件事

并行和并发,在编程中,一般称为并发

进程、线程、协程

进程

线程

协程

golang的线程模型

goroutine

程序启动,go为main函数创建一个goroutine

在main函数结束时,main函数里创建的goroutine都会结束

什么是channel

go并发模型,通信共享内存而不是共享内存实现通信。

共享内存,容易引起资源竞争,加锁会影响性能。

内置数据类型,类似队列,先进先出。

通道行为:创建、发送、接收、关闭

基本特性

类型安全:可以传输任何数据

缓冲:

同步

关闭

如何创建channel

go
ch := make(chan int)

// 带缓冲区的
chBuffered := make(chan int, 10)

channel的使用

发送数据到channel

从channel接收数据

不带缓冲channel

没有接收者,会deadLock

带缓冲channel示例

没有接收者,先缓冲,后阻塞

channel的关闭

go
close(ch)

使用range接收channel数据

go
for v := range ch{
    fmt.Println(v)
}

channel在并发中的应用

同步

通信

并行处理

示例:并发计算累加和

go
func main(){
    var m sync.Mutex
    numbers := []int{1,2,3,4,5}
    sum := 0
    ch := make(chan int)

    for _, num := range numbers {
        // 每一个数字启动一个goroutine,计算部分和 并发一起发送到channel
        // 然后使用range循环接收所有数据,打印最终的累加和
        go func(n int){
            m.Lock()
            sum += n
            ch <- sum
            m.Unlock()
        }(num)
    }

    var finalSum int
    for range numbers {
        finalSum <- ch
        fmtPrintln("当前总和sum:", finalSum)
    }

    fmtPrintln("最终总和sum:", finalSum)
}

select

go
func main() {
 ch := make(chan int)
 go func() {
  time.Sleep(3 * time.Second)
  ch <- 1
  close(ch)
 }()

 select {
 case data, ok := <-ch:
  if ok {
   fmt.Println("接收到数据: ", data)
  } else {
   fmt.Println("通道已被关闭")
  }
 case <-time.After(2 * time.Second):
  fmt.Println("超时了!")
 }

 time.Sleep(3 * time.Second)
}

竞争

go
var x int64
var wg sync.WaitGroup

// wg 好像有点不稳定,万一关了?
func add() {
 for i := 0; i < 5000; i++ {
  x = x + 1
 }
 // 线程执行完,需要结束
 wg.Done()
}
func main() {
 // wg 和 go 的数量有关系
 // 多了少了都不行
 wg.Add(2)
 //wg.Add(3)
 go add()
 go add()
 //go add()
 wg.Wait()
 fmt.Println(x)
}

加锁

读写锁

总结

go起线程很容易

上次更新于: