「Golang成长之路」并发之Goroutine

Golang
457
0
0
2022-06-11

Goroutine

一、并发

并发编程表现为程序有若干个自主的活动单元组成,在今天的互联网中,一个web服务器可能一次处理上千个请求,而平板电脑和手机在渲染用户界面的同时,后端还同步进行着计算和处理网络请求等。

在go语言中每一个并发执行的活动称之为goroutine,而在我们最常见的main函数中其实也是一个goroutine(主goroutine),在此之前,我们所见的介绍语法或者程序都是顺序执行的,但是在并发编程的领域里,如果从顺序编程获取的直觉可能让我们加倍迷茫。

在此我们需要理解什么是并发:

百科:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。

「Golang成长之路」并发之Goroutine

(这个是普通函数的运行模式,main将其控制权交给调用的函数,最后在返回给main)

我的理解:举个例子,我一边在上网课,一边在做笔记,我在做笔记的时候需要将网课暂停,等我的笔记做完后又继续看网课,这两件事就可以看作是一个并发的过程。

「Golang成长之路」并发之Goroutine

(协程则是:main和创建的goroutine是相互作用的,相互给予控制权,就像两个人,各做各的事,并且他们也相互通信)

二、go关键字

当一个程序启动时,只有一个goroutine来调用main,称他为主goroutine,新的goroutine通过go关键字来进行创建,看下面例子:

func main() {
   for i := 0; i < 3; i++ {
   //使用go关键字创建goroutine 
   //匿名函数 
       go func() {
          for j := 3; j >= 0; j-- {
            fmt.Println("gorouting", j)
          }
       }()
    fmt.Println("gorouting mian:", i)
    //控制程序运行时间
    time.Sleep(time.Microsecond)

   }
}

当然,也可以将匿名函数拿出来:

package main

import (
   "fmt" 
 "time")

func doWorker() {
     for j := 3; j >= 0; j-- {
        fmt.Println("gorouting", j)
     }
}

func main() {
   for i := 0; i < 3; i++ {
   //使用go关键字创建goroutine 
       go doWroker()
    fmt.Println("gorouting mian:", i)
    //控制程序运行时间
    time.Sleep(time.Microsecond)
   }
}

先来看看打印结果:

gorouting mian: 0
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 1
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 2
gorouting 3
gorouting 2
gorouting 1
gorouting 0

第二遍运行:

gorouting mian: 0
gorouting mian: 1
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 2
gorouting 3
gorouting 2
gorouting 1
gorouting 0

程序解释:其实可以看出,每一遍的运行结果都是不一样的,当程序加入第一个for , i=0时,运行到关键字go时,新的goroutine就创建了,但是程序不会立即执行新的goroutine,它会进行执行main中其余的代码,在我们的这个例子中,第一遍运行,时就是这个结果,第二遍运行时也是同样的。

func main(){
     var a[10] int
     for i := 0; i < 10;i++{
         //使用go关键字创建goroutine
         //匿名函数
          go func(i int){
             for{
                a[i]++
             }
          }(i)
       }
    time.Sleep(time.Microsecond)
    fmt.Println(a)
}

打印结果:

[39904 0 0 9122 0 7123 0 0 0 0]

程序解释:在for循环中一共创建了10个goroutine,有的goroutine执行了,有的还处于等待状态,但是我们给主goroutine的时间不多,所以有一些还来不及执行就被主goroutine杀掉了,这样我们就能理解打印结果了。

三、goroutine切换点(可能)

goroutine可能切换:

  1. I/O、select
  2. channel
  3. 等待锁
  4. 函数调用(有时)
  5. runtime.Gosched