并发编程笔记

并发编程

文章目录

  • 并发编程
    • @[toc]
      • 1.goroutine 协程
    • 代码演示
      • 2.调度器
      • 查看是否存在资源竞争

1.goroutine 协程

  • 语法:

    • 通过go+匿名函数启动goroutine

      • 代码演示

      func main() {
       for i := 0; i < 10; i++ {
         go func(i int) {
             for {
         	    fmt.Printf("%d", i)
             }
         }(i)
       }
      }
      
      • 以上代码在运行过程中什么都不会输出,由于main和并发内容同时执行,导致内容没有打印,程序就已经结束了
      • 解决方法:末尾添加一个休眠,其目的就是为了让main程序不要结束那么快
      func main() {
        for i := 0; i < 10; i++ {
          go func(i int) {
      	    for {
      		    fmt.Printf("%d", i)
      	    }
          }(i)
        }
          time.Sleep(2*time.Millisecond)//休眠两毫秒,将Millisecond替换成Second就是休眠两秒
      }
      
    • 通过go+有名函数启动goroutine

    • 代码演示

      func sum() {
        sum := 0
        for i := 0; i < 10000; i++ {
    	    sum += i
        }
        fmt.Println(sum)
      }
    
      func main() {
        for i := 0; i < 10; i++ {
    	    go sum()
        }
        fmt.Println("NumGoroutine=", runtime.NumGoroutine())
        time.Sleep(1 * time.Second)
      }
    
  • 协程特性

    • 轻量化"线程"
    • 非抢占式多任务处理,由协程主动交出控制权
    • 编译器/解析器/虚拟机层面的多任务
    • 多个线程可能在一个或多个线程上运行(由调度器执行)
    • 非阻塞的,不会等待
  • 几个有用的函数

函数 功能 返回值
runtime.GoMAXPROCS(n) 用来设置查询可以并发执行goroutine数目,n大于1表示设置,否则就是查询 整型数据int
runtime.Goexit 结束当前goroutine的运行
runtime.Gosched 放弃当前调度的执行机会(让出控制权),放到队列等待下一次别调度

2.调度器

  • Go 语言的调度器是一个运行时系统组件,它负责管理和调度所有的协程。在 Go 语言中,协程是一种轻量级的线程,它们的创建和销毁非常快速,并且可以同时运行数千个协程。Go 语言的调度器可以在多个操作系统线程上并发地执行协程,从而充分利用多核 CPU 的性能。

  • Go 语言的调度器采用的是 M:N 的协程调度模型,其中 M 个协程被映射到 N 个操作系统线程上执行。每个线程都会维护一个本地队列,用于保存当前线程运行的协程。当一个协程需要运行时,调度器会从全局队列中选取一个协程分配给空闲的线程执行。如果当前线程的队列中没有可用的协程,调度器会从其他线程的本地队列或全局队列中获取协程分配给当前线程执行。这种抢占式调度模型可以充分利用系统资源,从而实现高并发、高吞吐量的应用程序。

  • Go 语言的调度器还支持协程的阻塞和唤醒操作,以及垃圾回收等功能。当一个协程需要等待 I/O 操作或者其他协程时,它会被阻塞,并从线程的本地队列中移除。当条件满足时,调度器会唤醒这个协程并重新将它加入到线程的本地队列中。此外,调度器还会自动调用垃圾回收器来回收不再使用的内存,从而避免内存泄漏和内存溢出等问题。

  • 总之,Go 语言的调度器是一个非常重要的组成部分,它为开发者提供了一个高效、易用、稳定的协程编程模型。通过调度器的支持,开发者可以轻松地编写高并发、高吞吐量的应用程序,从而提高应用程序的性能和稳定性。


查看是否存在资源竞争

  • 终端输入:go build -race main.go,然后go run main.exe
  • 警告信息:WARNING DATA RACE则为资源竞争
  • 问题原因:多线程 并发
  • 解决方案:
    • 1.互斥锁(不太推荐)
      • 全局变量 通过加锁lock unlock 的方法 达到线程安全
      • lock sync.Mutex定义锁
      • lock.Lock() 锁起来
      • 中间代码
      • defer lock.Unlock() 最后释放锁
      • 弊端:依然无法预测线程和主线程的运行时间,有时候容易导致线程没打完就被杀死了
    • 2.channal通道(推荐)
      • chan 本质就是一个数据结构-队列
      • 先进先出 FIFO的规则,线程安全,多个Goroutine访问不需要加锁,因为通道本身线程安全
      • 注意: channel 是有类型的 定义存放的类型不能放不同类型 ,当然如果传空接口就能所有类型

你可能感兴趣的:(Go语言,笔记,算法,golang,开发语言,go,后端)