【青训营】Go的并发编程

news2025/1/7 18:39:27

本文章整理自——字节跳动青年训练营(第五届)后端组

1.线程和协程

操作系统中有三个重要的概念,分别是进程、线程和协程。其中进程和线程的区别请移步操作系统专栏,现在主要叙述线程和协程的区别。
简单来说,协程又称为用户态线程(以下的线程均指的是内核级线程),它比线程更加轻量化,使用起来更灵活,具有更高的性能。具体来说,协程的各种操作所需要的开销要比线程少,因此具有更高的性能。协程线程是内核态的,栈是MB级别的;协程是用户态的,其栈是KB级别的。一个线程可以控制多个协程,Go语言自动完成协程的创建,Go一次可以创建上万条协程,因此Go在针对高并发场景上有优势

Go使用如下方法创建协程

go func(形式参数){
	函数体
}(实际参数)

简单来说,就是在函数func前加go关键字以创建协程

协程的简单例子如下:

import (
	"fmt"
	"time"
)

// 协程的简单例子
func hello(i int) {
	println("Hello goroutine" + fmt.Sprint(i))
}

func main() {
	for i := 0; i < 5; i++ {
		// 在函数func前加go关键字以创建协程
		go func(j int) {
			hello(j)
		}(i) //此处填入协程的实际参数
	}
	time.Sleep(time.Second)
}

其输入如下:

Hello goroutine1
Hello goroutine4
Hello goroutine2
Hello goroutine0
Hello goroutine3

其数字i是完全随机的,可以看出协程的并发性

2.协程的通信CSP

GO提倡通过通信共享内存而不是使用共享内存通信,两者区别如下
在这里插入图片描述

3.通道Channel

通道是Go进行通信的重要手段,通道的创建如下

make(chan 元素类型, [缓冲区大小]// example
make(chan int)	//无缓冲通道
make(chan char, 2)

在这里插入图片描述
无缓冲通道中1发送的信息回立即传送到2中,会出现同步问题。而有缓冲的通道则会先放置到缓冲区中再送入2,带缓冲通道的可以解决一些速度不匹配问题

下面使用一个例子实现生产者消费者问题:
生产者:负责生成数字,并且将数字放入到缓冲区src中
消费者:从src中取出数字,并将其取平方
生产者消费者问题详解可以看这个:https://blog.csdn.net/weixin_45434953/article/details/127044788

package main

func main() {
	src := make(chan int)     // 无缓冲通道
	dest := make(chan int, 3) // 缓冲区为3的通道

	// 生产者协程,用于生产数字
	go func() {
		defer close(src)
		for i := 0; i < 10; i++ {
			src <- i //将数据i冲入通道src中
		}
	}()

	// 消费者进程,取出src中的数字并且将其平方后放入dist通道
	go func() {
		defer close(dest)
		for i := range src {
			dest <- i * i //将i的平方冲入dist通道中
		}
	}()

	for i := range dest {
		println(i)
	}
}

通过结果可以看出,通道可以保证输出的顺序

4.并发安全:锁Lock

使用锁可以确保对临界资源的互斥访问,从而避免同步互斥发生的数据不匹配问题,以下是一个简单的同步互斥问题:
我们需要启动5个协程,每个协程对x进行2000次+1的操作,x=x+1的操作可以分解为:1.运算器取得x的值 2.运算器执行x=x+1,并将结果写回寄存器 3.将运算器的值写回内存

import (
	"sync"
	"time"
)

var (
	x    int64
	lock sync.Mutex
)

// 加锁版本
func addWithLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x += 1
		lock.Unlock()
	}
}

// 不加锁版本
func addWithoutLock() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}

func main() {
	x = 0
	// 启动5个协程,他们需要互斥地访问x
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("WithLock:", x)
	x = 0
	// 启动5个协程,他们不需要互斥地访问x
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	println("WithoutLock:", x)
}

其输出的结果是:

WithLock: 10000
WithoutLock: 7050

分析以上代码片段:
如果使用addWithoutLock()函数,假设某时刻x=10,然后协程a执行了一次x=x+1,协程b执行了一次x=x+1,此时x的值应该是12。但是假设如下情况:协程a开始运行,根据分解操作,在执行到第二步的时候,cpu切换到执行协程b,此时协程a的寄存器中x=11,但是该值尚未写回内存,因此协程b从内存中取得的x的值还是10,进行x=x+1后,x=11并且将其写回内存,协程b执行结束,切换到未执行结束的协程a执行,此时a将寄存器中的x=11写回内存,则最终内存中x=11,显然这不是我们想要的结果,这就是同步导致的数据冲突,因此WithoutLock的结果最终是小于10000的

如果使用addWithLock函数,主要区别是,x只有在取得锁后才能对其进行操作,在进行x=x+1的之前,需要取得锁lock,执行结束后,才释放锁。这使得x=x+1的操作是一气呵成的,不会被中断的,这种又叫做原子操作。这避免了上述的状况。

5.WaitGroup

Go提供了WaitGroup来实现并发任务的同步,其中主要的三个方法:

Add(delta int)	//有多少个并发的协程
Done()		// 表示协程已完成,会将计数器的值-1
Wait()	//在计数器为0之前,一直阻塞不向下执行

这类似于一个计数器,刚开始使用Add表示有n个协程,而Done方法表示协程已完成,会将n–,当n!=0的时候,会一直触发Wait()方法,使得程序阻塞在Wait处;当n=0的时候表示所有协程均已完成,程序会继续执行Wait之后的语句

示例如下:

import (
	"fmt"
	"sync"
)

func hello(i int) {
	println("hello goroutine:" + fmt.Sprint(i))
}

func main() {
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func(j int) {
			defer wg.Done()
			hello(j)
		}(i)
	}
	wg.Wait()
}

这是对本文第一个例子的改写,没有使用time.Sleep(time.Second),因此性能更好

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

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

相关文章

看我们网络故障分析系统如何发现系统500报错

背景 汽车配件电子图册系统是某汽车集团的重要业务系统。业务部门反映&#xff0c;汽车配件电子图册调用图纸时&#xff0c;出现访问慢现象。 汽车集团总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对汽车配件电子图册…

Python学习笔记-网络爬虫基础

一、网络爬虫概述网络爬虫概述网络爬虫又称网络蜘蛛、网络机器人&#xff0c;在某社区中经常被称为网页追逐者。网络爬虫可以按照指定规则自动浏览或抓取网络中的信息&#xff0c;python可以很轻松的编写爬虫程序或脚本。网络爬虫基本工作流程&#xff1a;网络爬虫的常用技术2.…

【QT5 实现“上图下文”,带图标的按键样式-toolbutton-学习笔记-记录-基础样例】实现方式之一

【QT5 实现“上图下文”&#xff0c;带图标的按键样式-toolbutton-学习笔记-记录-基础样例】1、前言2、实验环境3、效果展示4、实验步骤第一步&#xff1a;新建工程-并运行。第二步&#xff1a;上网找图标文件第四步&#xff1a;&#xff08;非必须&#xff09;为了对比图标不同…

23种设计模式(三)——观察者模式【组件协作】

文章目录意图什么时候使用观察者使用观察者模式也有两个重点问题要解决&#xff1a;1&#xff09;广播链的问题2&#xff09;异步处理问题真实世界类比观察者模式的实现观察者模式的优缺点亦称&#xff1a;事件订阅者、监听者、Event-Subscriber、Listener、Observer 意图 在许…

mybatis之动态SQL测试环境的搭建以及if语句的使用

动态SQL&#xff1a; 动态 SQL 是 MyBatis 的强大特性之一&#xff0c;如果你使用过 JDBC 或其它类似的框架&#xff0c;你应该能理解根据不同条件拼接 SQL 语句有多痛苦&#xff0c;例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号&a…

Vue CLI

介绍 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统&#xff0c;提供&#xff1a; 通过 vue/cli 实现的交互式的项目脚手架。 通过 vue/cli vue/cli-service-global实现的零配置原型开发。 一个运行时依赖 (vue/cli-service)&#xff0c;该依赖&#xff1a; 可升级&a…

腾讯安全发布《2022年DDoS攻击威胁报告》:DDoS威胁4年持续增长

随着全球数字化蓬勃发展&#xff0c;互联网的应用范围不断扩大&#xff0c;并逐渐普及到各行各业的生产、管理、运营等方面&#xff0c;网络设备可用带宽伴随应用需求的增加而增加&#xff0c;方便了企业业务开展的同时也扩大了安全威胁面&#xff0c;引来黑产的觊觎。DDoS攻击…

Java使用流去除集合中某个字段为空的对象

文章目录0 写在前面1 情景复刻2 解决方案3 写在最后0 写在前面 最近写了一些业务逻辑&#xff0c;调试的时候总会报空指针异常。 Java中空指针异常是危险恐怖分子&#xff0c;最好不要碰见他。所以有些时候&#xff0c;处理集合中的数据时&#xff0c;特定情况下需要略过一些数…

十五天学会Autodesk Inventor,看完这一系列就够了(二),软件界面

众所周知&#xff0c;Autocad是一款用于二维绘图、详细绘制、设计文档和基本三维设计&#xff0c;现已经成为国际上广为流行的绘图工具。Autodesk Inventor软件也是美国AutoDesk公司推出的三维可视化实体模拟软件。因为很多人都熟悉Autocad&#xff0c;所以再学习Inventor&…

RK3568工业级核心板高温运行测试

Rockchip RK3568 是一款通用型MPU&#xff0c;产品集成GPU、NPU&#xff0c;支持4K、HDMI、LVDS、MIPI、PCIe3.0、USB3.0、千兆以太网、CAN-BUS、UART等丰富外设接口。 RK3568的高温工作情况如何呢&#xff1f;本文将基于万象奥科HD-RK3568-CORE 系列核心板做详细高温测试&…

接口幂等性设计

幂等性: 对于同一个操作发起一次请求或者多次请求&#xff0c;得到的结果都是一样的&#xff0c;不会因为请求多次而出现异常现象。 场景: 用户多次请求&#xff0c;比如重复点击页面上的按钮网络异常&#xff0c;右移网络原因导致在一定时间内未返回调用成功的信息&#xff…

《JavaScript 核心原理解析》学习笔记 Day 1 delete 引用与值

关于引用与值&#xff1a;在 javaScript 中一个表达式的值或者说结果&#xff0c;可能是引用 / 值。所以 x x &#xff0c;是将右侧表达式x的值赋值给左侧表达式x所指的引用。注意此处的引用并非为到具体内存地址的指向&#xff0c;而是指表达式与其值的一种关联。 这一关联即…

Android 音视频——直播推流技术指南

一、推流架构 推流SDK客户端的模块主要有三个&#xff0c;推流采集端、队列控制模块、推流端。其中每个模块的主要流程如下&#xff0c;本文的主要目的就是拆分推流流程&#xff0c; 1.1 采集端 视频采集&#xff1a;通过Camera采集视频。 音频采集&#xff1a;通过麦克风采…

SSM 05 SpringBoot yaml mybatisplus

01-SpringBoot工程入门案例开发步骤SpringBoot 是 Pivotal 团队提供的全新框架&#xff0c;设计目的是简化 Spring 应用的初始搭建以及开发过程。使用了 Spring 框架后已经简化了我们的开发。而 SpringBoot 又是对 Spring 开发进行简化的&#xff0c;可想而知 SpringBoot使用的…

linux挂载新磁盘

一、查看磁盘挂载状态&#xff1a; fdisk -l df -h 二、为其中一个磁盘创建新的分区&#xff0c;参考&#xff1a; linux用fdisk创建分区,在Linux下用fdisk创建分区_weixin_39968410的博客-CSDN博客 sudo fdisk /dev/nvme0n1 1. 创建主分区&#xff1a; -----------------…

第8章 NVS

NVS Blob块存储 1. 演示app_main任务栈溢出 2. 设置app_main任务栈大小 打开menuconfig&#xff0c;输入main&#xff0c;如下图所示 默认栈大小为3584字节&#xff0c;这里改为35840字节&#xff0c;重新编译 3. Blob存储结果 #include <stdio.h> #include <st…

使用nginx搭建HTTP FLV流媒体服务器

使用nginx搭建HTTP FLV流媒体服务器 文章目录使用nginx搭建HTTP FLV流媒体服务器1 HTTP FLV简介2 HTTP FLV流媒体服务搭建3 结果验证1 HTTP FLV简介 前文已经介绍了RTSP、RTMP、HLS的流媒体协议&#xff0c;还有一种比较常见的流媒体协议HTTP FLV&#xff0c;其兼具RTMP的实时…

Kettle源码启动运行

Kettle源码运行环境如下&#xff1a; windows10 Kettle 9.3.0.2 Java JDK 11 IntelliJ IDEA 2021.2.2 (Community Edition) Maven 3.8.1&#xff08;版本不需要太高 &#xff09; 导入kettle到IDEA 可通过kettle的GIthub地址获取 kettle的克隆连接&#xff0c;或直接下载ZIP压…

python2和python3环境安装

一、背景 ​ 众所周知&#xff0c;python当前有两大主流版本&#xff0c;分别是Python2和Python3系列&#xff0c;其中Python3因为对Python2做了较大的优化&#xff0c;使得Python3不会向下兼容&#xff0c;但是工作和学习中&#xff0c;有很多项目需要Python2的环境&#xff…

SAP 物料账未分摊差异分析

今天在开发处理未分摊差异程序的时候&#xff0c;偶然在网络上看到一篇这样的文章&#xff0c;挺有意思的&#xff0c;特意转载过来&#xff0c;方便大伙学习之用&#xff0c;若有异议&#xff0c;立即撤回。 利用CKMLCP运行完物料分类账之后&#xff0c;差异科目余额通常为0&…