Go语言并发

news2025/1/11 2:34:50

Go语言并发学习目标

出色的并发性是Go语言的特色之一

  • • 理解并发与并行
  • • 理解进程和线程
  • • 掌握Go语言中的Goroutine和channel
  • • 掌握select分支语句
  • • 掌握sync包的应用

并发与并行

并发与并行的概念这里不再赘述,
可以看看之前java版写的并发实践;

进程和线程

程序、进程与线程这里也不赘述
一个进程可以包括多个线程,线程是容器中的工作单位;

协程~Goroutine

概念:

协程(Coroutine),最初在1963年被提出,又称为微线程,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程,一个线程也可以拥有多个协程;

协程是编译器级的进程和线程是操作系统级的

协程不被操作系统内核管理,而完全由程序控制,因此没有线程切换的开销。和多线程比,线程数量越多,协程的性能优势就越明显。协程的最大优势在于其轻量级,可以轻松创建上万个而不会导致系统资源衰竭。

Go语言中的协程

Go与语言中的协程由运行时调度和管理,Go会智能将协程中的任务合理的分配给每个CPU;

一开始创建的协程堆栈很小,但是可以根据需要增长和收缩;

Coroutine与Goroutine

Goroutine能并行执行,Coroutine只能顺序执行,Go中Goroutine可以在单线程中产生,也可以在多线程中产生;

Coroutine程序需要主动交出控制权,系统才能获得控制权并将控制权交给其他Coroutine.

Coroutine属于协作处理,在应用程序不使用CPU时,需要让渡CPU,否则会使得计算机失去响应或者宕机;

Goroutine属于抢占式任务处理,和现有的多线程和多进程任务处理类似;应用程序对CPU的控制最终由操作系统来管理,如果操作系统发现一个应用程序长时间占用CPU,那么用户有权终止这个任务。

在Go中开启协程

在Go中开启协程----只需要在函数前面加上关键字go,将会同时运行一个新的Goroutine;

注意:使用go关键字创建协程时,被调用的函数往往没有返回值,如果函数有返回值,那么返回值会被忽略,那我们就是要返回值时,必须使用channel,通过channel把数据从中取出来;

Go程序的执行过程

创建和启动主Goroutine,初始化操作,执行main函数,当main函数执行结束后,程序也就结束了;

代码demo

func helloworld() {
	fmt.Println("hello world")

}
func main() {
	go helloworld()
	fmt.Println("main exit")
}

运行结果:
在这里插入图片描述
但是这可能并不是我们看到的全部,如果main()的Goroutine比子Goroutine后终止,那么我们就会看到打印出的hello world;

多试几次(直接运行应该还是上面的结果,如果debug,就会出现打印hello world,这是因为协程有足够的时间反应;
在这里插入图片描述

我们来看一下如果加上睡眠时间:

func helloworld() {
	fmt.Println("hello world")

}
func main() {
	go helloworld()
	time.Sleep(10 * time.Microsecond)
	fmt.Println("main exit")
}

运行结果:
在这里插入图片描述
如果我们在fmt.Println("main exit")前加上defer 会是什么结果?会打印 hello world吗?

我试了一下,不行:

在这里插入图片描述
我们来看一下defer 关键字的释义

“defer”语句调用一个函数,该函数的执行被延迟到周围的函数返回的那一刻,要么是因为周围的函数执行了return语句,到达了它的函数体的末尾,要么是因为对应的协程出现了恐慌。

所以下面的代码运行结果是什么?

代码demo3

func helloworld() {
	fmt.Println("hello world")
}
func helloworld2() string {
	fmt.Println("hello world222222")
	return "hello world222222"
}

func main() {
	go helloworld() //要留有足够的时间,否则main()的Goroutine终止了,程序将被终止,该协程根本就没有机会表现
	defer helloworld2()
	defer fmt.Println("main exit")

}

在这里插入图片描述
我们来分析一下 程序的执行(即代码demo3):

go程序启动时,runtime默认为main函数创建一个Goroutine;

在这里插入图片描述

在main函数的Goroutine执行到 go helloworld()即加了关键字go的方法时.归属于helloworld()函数的Goroutine被创建,helloworld()函数开始在自己的Goroutine中执行,

此时main函数的Goroutine继续执行,如果helloworld()函数不能够在main函数的Goroutine执行完毕之前将任务处理完毕,那么就会发生helloworld()函数没有执行的样子 ;

下面我们来修改上面的代码:

func helloworld() {
	var i int
	for {
		i++
		fmt.Println("add", i)
		time.Sleep(time.Second)
	}
}
func main() {
	go helloworld() //要留有足够的时间,否则main()的Goroutine终止了,程序将被终止,该协程根本就没有机会表现
	var str string
	fmt.Scanln(&str) //阻塞
	fmt.Println("main exit")
}

控制台不断输出add int,同时还可以接收用户输入。两个环节同时运行。
在这里插入图片描述

此时,main()继续执行,两个Goroutine通过Go程序的调度机制同时运行。

匿名函数创建Goroutine

即我们可以在匿名函数前加go关键字实现对匿名函数创建Goroutine;

func main() {
	go func() {
		var arr int
		for {
			arr++
			fmt.Println("add", arr)
			time.Sleep(time.Second)
		}
	}() //闭包

	var str string
	fmt.Scanln(&str)
	fmt.Println("main exit")
}

在这里插入图片描述

启动多个Goroutine


func p1() {
	for i := 0; i < 10; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Print(i)
	}
}
func p2() {
	for i := 'a'; i < 'i'; i++ {
		time.Sleep(500 * time.Millisecond)
		fmt.Printf("%c", i)
	}
}

func main() {
	go p1()
	go p2()
	var str string
	fmt.Scanln(&str)
	fmt.Println("main exit")
}

多个Goroutine随机调度,打印的结果是数字与字母交叉输出
在这里插入图片描述

并发性能优化

在Go程序运行时,go 关键字实现了一个小型的任务调度器,该调度器是使用CPU的,那么怎么为其分配CPU呢 ?

go中可以使用runtime.Gosched()来交出CPU的控制权,同时我们可以使用runtime.GOMAXPROCS()来匹配CPU核心数量

  • Go1.5版本之前,默认使用单核执行。
  • Go1.5版本开始,默认执行runtime.GOMAXPROCS(逻辑CPU数量),让代码并发执行,最大效率地利用CPU。
//Gosched生成处理器,允许其他goroutines运行。它不是挂起当前的goroutine,这个就像java中的yield(),只是让出cpu,但同时我还可以跟其他协程竞争cpu
func Gosched() {
	checkTimeouts()
	mcall(gosched_m)
}

// GOMAXPROCS设置可以执行的cpu的最大数目同时返回之前的设置。默认为 runtime.NumCPU的值。如果n < 1,则不改变当前设置。
//当调度器改进时,此调用将消失。
func GOMAXPROCS(n int) int {
	if GOARCH == "wasm" && n > 1 {
		n = 1 // WebAssembly has no threads yet, so only one CPU is possible.
	}

	lock(&sched.lock)
	ret := int(gomaxprocs)
	unlock(&sched.lock)
	if n <= 0 || n == ret {
		return ret
	}

	stopTheWorldGC("GOMAXPROCS")

	// newprocs will be processed by startTheWorld
	newprocs = int32(n)

	startTheWorldGC()
	return ret
}

Channel 通道

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/580671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言3:根据身份证号输出生年月日和性别

18位身份证号码第7到10位为出生年份(四位数)&#xff0c;第11到12位为出生月份&#xff0c;第13 到14位代表出生日期&#xff0c;第17位代表性别&#xff0c;奇数为男&#xff0c;偶数为女。 用户输入一个合法的身份证号&#xff0c;请输出用户的出生年月日和性别。(不要求较验…

Java数据结构之第十三章、字符串常量池

目录 一、创建对象的思考 二、字符串常量池(StringTable) 三、再谈String对象创建 一、创建对象的思考 下面两种创建String对象的方式相同吗&#xff1f; public static void main(String[] args) {String s1 "hello";String s2 "hello";String s3 …

C# | 线性回归算法的实现,只需采集少量数据点,即可拟合整个数据集

C#线性回归算法的实现 文章目录 C#线性回归算法的实现前言示例代码实现思路测试结果结束语 前言 什么是线性回归呢&#xff1f; 简单来说&#xff0c;线性回归是一种用于建立两个变量之间线性关系的统计方法。在我们的软件开发中&#xff0c;线性回归可以应用于数据分析、预测和…

每日一博 - 对称加密算法 vs 非对称加密算法

文章目录 概述一、对称加密算法常见的对称加密算法优点&#xff1a;缺点&#xff1a;Code 二、非对称加密算法常见的非对称加密算法优点&#xff1a;缺点&#xff1a;Code 概述 在信息安全领域中&#xff0c;加密算法是保护数据安全的重要手段。 加密算法可以分为多种类型&am…

【Linux】线程互斥 与同步

文章目录 1. 背景概念多个线程对全局变量做-- 操作 2. 证明全局变量做修改时&#xff0c;在多线程并发访问会出问题3. 锁的使用pthread_mutex_initpthread_metux_destroypthread_mutex_lock 与 pthread_mutex_unlock具体操作实现设置为全局锁 设置为局部锁 4. 互斥锁细节问题5.…

哈夫曼树(Huffman)【数据结构】

目录 ​编辑 一、基本概念 二、哈夫曼树的构造算法 三、哈夫曼编码 假如<60分的同学占5%&#xff0c;60到70分的占15%…… 这里的百分数就是权。 此时&#xff0c;效率最高&#xff08;判断次数最少&#xff09;的树就是哈夫曼树。 一、基本概念 权&#xff08;we…

Zabbix4.0 自动发现TCP端口并监控

java端口很多&#xff0c;每台机器上端口不固定&#xff0c;考虑给机器配置组不同的组挂载模版&#xff0c;相对繁琐。直接使用同一个脚本自动获取机器上java相关的端口&#xff0c;推送到zabbix-server。有服务端口挂了自动推送告警 一、zabbix-agent配置过程 1、用户自定义参…

Apache Doris :Rollup 物化视图

整理了一下目前开启虚拟机需要用到的程序, 包括MySQL,Hadoop,Linux, hive,Doris 3.5 Rollup ROLLUP 在多维分析中是“上卷”的意思&#xff0c;即将数据按某种指定的粒度进行进一步聚合。 1.求每个城市的每个用户的每天的总销售额 select user_id,city,date&#xff0c; sum(…

树的简单介绍

目录 树的概念 ​ 树的相关概念 树的表示 二叉树的概念 特殊的二叉树 二叉树的存储结构 总结 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#…

Diffie-Hellman密钥交换协议(Diffie-Hellman Key Exchange,简称DHKE)

文章目录 Diffie-Hellman密钥交换协议&#xff08;Diffie-Hellman Key Exchange&#xff0c;简称DHKE&#xff09;一、密码学相关的数学基础1. 素数&#xff08;质数&#xff09;2. 模运算3. 费马小定理4. 对数5. 离散对数6. 椭圆曲线常见椭圆曲线1. NIST系列曲线secp256k1 2. …

Django实现人脸识别登录

Django实现人脸识别登录 Demo示例下载 1、账号密码登录 2、人脸识别登录 3、注册 4、更改密码 5、示例网站 点我跳转 一、流程说明 1、注册页面:前端打开摄像头,拍照,点击确定后上传图像 2、后端获取到图像,先通过face_recognition第三方库识别是否能够获取到人脸特征…

开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f649;。 …

使用腾讯云服务器快速搭建网站教程

已经有了腾讯云服务器如何搭建网站&#xff1f;腾讯云服务器网以腾讯云服务器&#xff0c;借助宝塔面板搭建Web环境&#xff0c;然后使用WordPress博客程序搭建网站&#xff0c;大致分为三步&#xff0c;首先购买腾讯云服务器&#xff0c;然后在腾讯云服务器上部署宝塔面板&…

Vue + Vite 构建 自己的ChartGPT 项目

前期回顾 两分钟学会 制作自己的浏览器 —— 并将 ChatGPT 接入_彩色之外的博客-CSDN博客自定义浏览器&#xff0c;并集合ChatGPT&#xff0c;源码已公开https://blog.csdn.net/m0_57904695/article/details/130467253?spm1001.2014.3001.5501 目录 效果图 代码步骤&am…

【Linux】软件包管理器/编辑器/yum是应用商店?/vim编辑器什么?

本文思维导图&#xff1a; 文章目录 Linux软件安装关于Linux的软件生态 1.Linux软件包管理器&#xff1a;yum到底是什么关于yum指令&#xff1a;关于yum源 2. rzsz指令1. Linux编辑器——vim编辑器vim编辑器的三种主要模式vim编辑器命令模式常用快捷键&#xff1a;vim操作总结…

spring(事务管理)

事物可以看做是由对数据库若干操作组成的一个单元 事务的作用就是为了保证用户的每一个操作都是可靠的&#xff0c;事务中的每一步操作都 必须成功执行&#xff0c;只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成&#xff0c;要么都取消&#xff0c;从而…

Linux:/dev/tty、/dev/tty0 和 /dev/console 之间的区别

在Linux操作系统中&#xff0c;/dev/tty、/dev/tty0和/dev/console是三个特殊的设备文件&#xff0c;它们在终端控制和输入/输出过程中扮演着重要的角色。尽管它们看起来很相似&#xff0c;但实际上它们之间存在一些重要的区别。本文将详细介绍这三个设备文件之间的区别以及它们…

浅谈如何fltk项目编译和实现显示中文

目录 一、编译 二、中文显示如何处理&#xff1a; 2.1在发文2天前突然发现&#xff0c;我这个界面显示英文出现问题了&#xff0c;开始我的搜索之旅&#xff0c;一些参考页面有碰到问题也可以看看&#xff1a; 2.2、 那就开始翻翻官方自带的例程吧&#xff0c;看看他如何显…

Join的连接原理

1. 连接简介 1.1 连接的本质 连接就是把各个表中的记录都取出来进行一次匹配&#xff0c;并把匹配后的组合发送给客户端。如果连接查询中的结果集中包含一个表中的每一条记录与另一个表中的每一条记录相互匹配的组合&#xff0c;那么这样的结果集就可以称为笛卡尔积。 1.2 连…

计算机网络基础知识(七)—— 什么是HTTPS协议?你听我“瞎掰”

文章目录 01 | 工作原理02 | SSL/TLS协议2.1 | 握手协议2.2 | 更换密码协议&#xff08;Change Cipher Spec Protocol&#xff09;2.3 | 警告协议&#xff08;Alert Protocol&#xff09;2.4 | 应用数据协议&#xff08;Application Data Protocol&#xff09; 03 | 加密算法3.…