golang笔记10--go语言并发编程模块 goroutine

golang笔记10--go语言并发编程模块 goroutine

1 介绍

本文继上文 golang笔记09–go语言测试与性能调优, 进一步了解 go语言的并发编程模块 --goroutine,以及相应注意事项。
具体包括 : goroutine 和 go语言的调度器 等内容。

2 Goroutine

2.1 goroutine

主流的并发模型包括:多进程、多线程、基于回调的非阻塞|异步IO、协程 等方式,其优缺点如下:

并发模型基本原理优点缺点
多进程操作系统层面进行并发的基本模式简单、进程见互不影响系统开销大
多线程也属于操作系统层面的并发模型,但其比进程效率高开销低,可提高 cpu 内存利用率 和 执行效率线程会占用一定资源,使用过多会降低程序性能;程序设计比多进程复杂
非阻塞|异步IO通过事件驱动的方式进行IO,CPU执行指令后不需要立即返回,过段时间执行完成后通知cpu继续处理相关任务能够降低线程使用两,适用于IO密集操作编程相对复杂
协程本质是用户态的线程(轻量级线程),不需要操作系统进行抢占式调度,属于编译器|解释器|虚拟机层面的多任务,多个协程可以在一个或多个线程上运行开销极小能提高并发性,编程简单结构清晰需要特定语言的支持,若不支持则小自行实现调度器

go 语言原生支持轻量级线程,即 goroutine,它由go运行时管理。 Go语言从初始化main package 并执行main() 函数开始,当mai()函数返回时程序退出,且程序不等待其它 goroutine(非主goroutine) 结束。

以下案例中通过1000 个 goroutine 输出当前时间 + hello 信息,为了避免主 goroutine 退出,当前使用较粗暴的方式直接 sleep 一分钟。

vim 10.1.go
package main

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

func main() {
	/*
		for i := 0; i < 1000; i++ {
			go func(i int) {
				for {
					fmt.Printf("%s hello,i'm from goroutine %d\n", time.Now().String(), i)
				}
			}(i)
		}
		time.Sleep(time.Minute)
	*/
	var a [10]int
	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				a[i]++
				runtime.Gosched()
			}
		}(i)
	}
	time.Sleep(time.Second)
	fmt.Println("goroutine a[i]++")
	fmt.Println(a)
}

输出(1)2021-02-17 11:41:06.938943939 +0800 CST m=+3.550203203 hello,i'm from goroutine 469
2021-02-17 11:41:06.663614907 +0800 CST m=+3.274874166 hello,i'm from goroutine 5
2021-02-17 11:41:07.164039483 +0800 CST m=+3.775298734 hello,i'm from goroutine 469
2021-02-17 11:41:07.16404103 +0800 CST m=+3.775300290 hello,i'm from goroutine 5
2021-02-17 11:41:07.069884497 +0800 CST m=+3.681143750 hello,i'm from goroutine 785
......
输出(2):
goroutine a[i]++
[575155 549057 578855 550274 575947 557208 537390 548969 563552 539226]

注意:
此处使用函数式编程,必须将 i 作为参数传入;若不传入i,则函数闭包,会使用外部的i,最终导致使用到 a[10] 报错;
程序运行结果不达预期,可能原因为存在数据冲突,此时可以通过 go run -race 10.1.go 的形式来检测。

2.2 go语言的调度器

从程序角度来看,子程序是协程的一个特例,子程序代表的普通函数和协程的对比如下,从图中可见协程包含了普通函数:
在这里插入图片描述
从调度角度来看,一个或者多个协程都有可能被调度到某个线程中,其由调度器决定,一般情况下不需要用户关心。
在这里插入图片描述
goroutine 的定义:

  • 任何函数只需要加上go就能送给调度器运行;
  • 不需要在定义时区分是否是异步函数;
  • 调度器在合适的点进行切换;
  • 使用 -race 来检查数据访问冲突;

goroutine 可能的切换点:

  • I/O, select
  • channel
  • 等待锁
  • 函数调用(有可能)
  • runtime.Gosched()

3 注意事项

  1. Go语言从初始化main package 并执行main() 函数开始,当mai()函数返回时程序退出,且程序不等待其它 goroutine(非主goroutine) 结束;因此可以使用sleep 等简单粗暴的方式避免程序退出。

4 说明

  1. 软件环境
    go版本:go1.15.8
    操作系统:Ubuntu 20.04 Desktop
    Idea:2020.01.04
  2. 参考文档
    由浅入深掌握Go语言 --慕课网
    go 语言编程 --许式伟
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昕光xg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值