GO 浅谈goroutine(总结于尚硅谷go)

GO并发编程问题

并行和并发

并行(parallel):(并行就是两个队列同时使用2个厕所)

指在同一时刻(CPU时间量级 ),有`多条指令`在`多个处理器`上同时执行。需要借助多核CPU来实现

在这里插入图片描述

并发:(就是两个队列交替使用一间厕所)

宏观:用户体验上,程序在并行执行。  
微观:多个计划任务,顺序执行。在飞快的切换。轮换使用 cpu 时间轮片。    (类似于串行)
进程并发
程序和进程?
程序:编译成功得到的二进制文件。	占用 磁盘空间。	死的	  1	   1
进程:运行起来程序。 占用系统资源。(内存)		      活的    N	 1
可以理解成:
程序 → 剧本(纸)		进程 → 戏(舞台、演员、灯光、道具...)

进程状态

进程基本的状态有5种。分别为初始态、就绪态、运行态、挂起(阻塞)态、终止(停止)态。其中初始态为进程准备阶段,常与就绪态结合来看。

在这里插入图片描述

同步:协同步调。规划先后顺序。
线程同步机制:
    互斥锁(互斥量):建议锁。 拿到锁以后,才能访问数据,没有拿到锁的线程,阻塞等待。等到拿锁的线程释放锁。
    读写锁:一把锁(读属性、写属性)。 写独占,读共享。 写锁优先级高。

Go语言中的并发程序主要使用两种手段来实现。goroutinechannel

goroutine 又叫做go程 创建于进程中,直接使用 go 关键,放置于 函数调用前面,产生一个 go程。 并发

package main

import (
    "fmt"
    "time"
)

func newTask() {
    i := 0
    for {
        i++
        fmt.Printf("new goroutine: i = %d\n", i)
        time.Sleep(1 * time.Second) //延时1s
    }
}

func main() {
    //创建一个 goroutine,启动另外一个任务
    go newTask()
    i := 0
    //main goroutine 循环打印
    for {
        i++
        fmt.Printf("main goroutine: i = %d\n", i)
        time.Sleep(1 * time.Second) //延时1s
    }
}

Goroutine的特性:【重点】主go程结束,子go程随之退出

package main

import (
"fmt"
"time"
)

func newTask() {
    i := 0
    for {
        i++
        fmt.Printf("new goroutine: i = %d\n", i)
        time.Sleep(1 * time.Second) //延时1s
    }
}

func main() {
    //创建一个 goroutine,启动另外一个任务
    go newTask()

    fmt.Println("main goroutine exit")
}

runtime包

  • runtime.Gosched():
    出让当前go程所占用的 cpu时间片。当再次获得cpu时,从出让位置继续回复执行。
    —— 时间片轮转调度算法。
package main

import (
"fmt"
"runtime"
)

func main() {
    //创建一个goroutine
    go func(s string) {
        for i := 0; i < 2; i++ {
            fmt.Println(s)
        }
    }("world")

    for i := 0; i < 2; i++ {
        runtime.Gosched()  //import "runtime" 包
        /*
            屏蔽runtime.Gosched()运行结果如下:
                hello
                hello

            没有runtime.Gosched()运行结果如下:
                world
                world
                hello
                hello
        */
        fmt.Println("hello")
    }
}

以上程序的执行过程如下:
主协程进入main()函数,进行代码的执行。当执行到go func()匿名函数时,创建一个新的协程,开始执行匿名函数中的代码,主协程继续向下执行,执行到runtime.Gosched( )时会暂停向下执行,直到其它协程执行完后,再回到该位置,主协程继续向下执行。

  • runtime.Goexit():
    return: 返回当前函数调用到调用者那里去。 return之前的 defer 注册生效。
    Goexit(): 结束调用该函数的当前go程。Goexit():之前注册的 defer都生效。
package main

import (
"fmt"
"runtime"
)

func main() {
    go func() {
        defer fmt.Println("A.defer")

        func() {
            defer fmt.Println("B.defer")
            runtime.Goexit() // 终止当前 goroutine, import "runtime"
            fmt.Println("B") // 不会执行
        }()

        fmt.Println("A") // 不会执行
    }() 	//不要忘记()

    //死循环,目的不让主goroutine结束
    for {
    }
}
  • GOMAXPROCS
    调用 runtime.GOMAXPROCS() 用来设置可以并行计算的CPU核数的最大值,并返回之前的值。
package main

import (
    "fmt"
)

func main() {
//n := runtime.GOMAXPROCS(1) 	// 第一次 测试
//打印结果:111111111111111111110000000000000000000011111...

n := runtime.GOMAXPROCS(2)         // 第二次 测试
//打印结果:010101010101010101011001100101011010010100110...
    fmt.Printf("n = %d\n", n)

    for {
        go fmt.Print(0)
        fmt.Print(1)
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210221145313977.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BwcHBwcHVzaGNhcg==,size_16,color_FFFFFF,t_70)
}
}

在第一次执行runtime.GOMAXPROCS(1) 时,最多同时只能有一个goroutine被执行。所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。
在第二次执行runtime.GOMAXPROCS(2) 时, 我们使用了两个CPU,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。

调用 runtime.GOMAXPROCS() 用来设置可以并行计算的CPU核数的最大值,并返回之前(上一个)的值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值