go语言多线程与并发编程

2023-05-16

go语言并发编程

在了解go语言的并发编程之前,我们必须先了解并发和并行的概念。我们知道,当启动一个应用的时候实际上是启动了一个进程,通过该进程实现资源的调度和分配,并且多个进程之间是相互隔离的,所以我们运行其中一个应用不会对其他应用造成影响。

默认情况下一个进程只有一个线程,也就是单线程应用,这种模式下所有的操作都是同步的,处理完A事件以后才能接着处理B事件再接着处理C事件。

那么,单线程模式下能否进行并发编成,当然可以。我们设想一下,我有A、B、C三件事情要做,但是我又不想一个一个同步的去做,这时候我们可以使用并发模式,我先做A的一部分,接着做B的一部分,然后是C的一部分,通过这种不停切换的方式可以保证三件事情同时推进,这就算并发。

在多核处理器之前,也就是单核时代,我们只能在一个线程之间进行多任务的调度,这种调度方式也就算并发,随之技术的进步,现在已经进入了多核时代,为计算机的并行开发提供了条件。

我们在设想一下,以前就只有自己一个人,现在我又多个几个双胞胎兄弟(多核),那我是不是可以再请两个兄弟帮着做B、C事情,我自己做A事情,这样同时推进A、B、C事情,是的,这就是并行。

有时候并发和并行是同时的,比如多个线程同时处理数百件任务,多个线程并行的执行任务,一个线程又并发的执行多个任务。

言归正传,接下来我们开始分析go语言的并发编程。

1、goroutine

package main

import (

"fmt"

"time"

)

func printString(value string) {

for i := 0; i < 10; i++ {

fmt.Println(value)

time.Sleep(time.Second) //0.1 second

}

}

func main() {

go printString("A")

go printString("B")


time.Sleep(time.Second * 10)//暂时挂起主线程,让goroutine的两个线程跑完

}

输出结果:ABABBAABABBABABAABAB

两个线程交叉输出,说明是以并发形式执行,这里为什么说是并发而不是并行,因为go语言默认是单核执行线程。如果想要多核并行执行,需要在启动goroutine之前首先调用以下语句配置cpu核数:

runtime.GOMAXPROCS()

自从Go 1.5开始, Go的GOMAXPROCS默认值已经设置为 CPU的核数,我们不用手动设置这个参数。

2、go语言的线程通信

无论是哪种编程语言,实现多线程之间的通信方式无非是共享数据和消息两种方式。

1)我们先看下go语言是如何通过共享数据实现线程通信。

package main

import (

"fmt"

"sync"

"time"

)

var counter int = 0

func Count(lock *sync.Mutex) {

lock.Lock()

counter++

fmt.Println(counter)

lock.Unlock()

}

func main() {

lock := &sync.Mutex{}

for i := 0; i < 5; i++ {

go Count(lock)

}

time.Sleep(time.Second * 5)

}

输出结果:1 2 3 4 5

此时,多个线程共享数据counter,实际上当业务逻辑比较复杂并且共享数据比较多的情况下,使用这种方式是一件十分头疼的事情。我们这里暂且不提这些。

2)消息机制实现多线程通信

go语言使用channel在两个或多个线程之间传递消息。channel翻译成中文就算通道的意思,我们直接看代码:

package main

import (

"fmt"

"sync"

"time"

)

func WriteIn(ch chan int) {

for i := 0; i < 10; i++ {

ch <- i

}

}

func ReadOut(ch chan int) {

for i := 0; i < 10; i++ {

value := <-ch

fmt.Println(value)

}

}

func main() {

ch := make(chan int)

go WriteIn(ch)

go ReadOut(ch)

time.Sleep(time.Second * 5)

}

输出:1 2 3 4 5 6 7 8 9,通过chanel在不需要锁的情况下实现了数据的共享,chanel的用法中,常见的操作包括写入和读出,写入和读出都会导致线程的阻塞,也就是说数据写入后线程会等待另一个线程的读取,而读取之前如果没有数据写入也会进入阻塞状态等待数据写入。

3、go语言的线程同步

1)使用互斥锁实现线程同步

互斥锁是最简单的一种锁类型,同时也比较暴力,当一个goroutine获得了锁之后,其他goroutine就只能乖乖等到这个goroutine释放该锁。go语言使用sync.Mutex实现互斥锁。

代码如下:

package main

import (

"fmt"

"sync"

"time"

)

type MutexInfo struct {

mutex sync.Mutex

infos []int

}

func (m *MutexInfo) addInfo(value int) {

m.mutex.Lock()

m.infos = append(m.infos, value)

m.mutex.Unlock()

}

func main() {

m := MutexInfo{}

for i := 0; i < 10; i++ {

go m.addInfo(i)

}

time.Sleep(time.Second * 5)

fmt.Println(m.infos)

}

输出:[0 1 2 3 4 5 6 7 8 9]

我们通过多次运行发现,输出的结果并不总是从0到9按顺序输出,说明创建的10个goroutine并不是有序的抢占线程的执行权,也就是说这种同步并不是有序的同步,我们可以让10个goroutine一个一个的同步执行,但是并不能安排执行次序。

运行到这里,假如我们注释掉同步锁的代码为发生什么?

我们将addInfo方法修改如下:

func (m *MutexInfo) addInfo(value int) {

//m.mutex.Lock()

m.infos = append(m.infos, value)

//m.mutex.Unlock()

}

运行代码,输出:[1 0 2]

结果是不是出乎意料?为什么写了10个输入,只有3个值输入成功?这时候我们不得不解释线程的另一个概念,那就是线程安全。

我们先看下go语言中slice的append过程,使用append添加一个元素时,可能会有两步来完成:先获取当前切片数组的容量,比如当前容量是2,然后在新的存储区开辟一块新的存储单元,容量为2+1,并将原来的值和新的值存入新的存储单元。在没有同步锁的情况下,如果两个线程同时执行添加元素的操作,这时候可能只有一个被写入成功。这种情况就是非线程安全,相比之下,如果同时对一个int类型数据进行操作,就不会出现这种非线程安全的情况。

2)读写锁实现线程同步

go语言提供了另一种更加友好的线程同步的方式:sync.RWMutex。相对于互斥锁的简单暴力,读写锁更加人性化,是经典的单写多读模式。在读锁占用的情况下,会阻止写,但不阻止读,也就是多个goroutine可同时获取读锁,而写锁会阻止其他线程的读写操作。

代码如下:

package main

import (

"fmt"

"sync"

"time"

)

type MutexInfo struct {

mutex sync.RWMutex

infos []int

}

func (m *MutexInfo) addInfo(value int) {

m.mutex.Lock()

defer m.mutex.Unlock()

fmt.Println("write start", value)

m.infos = append(m.infos, value)

fmt.Println("write start end", value)

}

func (m *MutexInfo) readInfo(value int) {

m.mutex.RLock()

defer m.mutex.RUnlock()

fmt.Println("read start", value)

fmt.Println("read end", value)

}

func main() {

m := MutexInfo{}

for i := 0; i < 10; i++ {

go m.addInfo(i)

go m.readInfo(i)

}

time.Sleep(time.Second * 3)

fmt.Println(m.infos)

}

输出结果:

read start 0

read end 0

write start 1

write start end 1

read start 4

read end 4

read start 9

read end 9

read start 3

read start 1

read start 7

read end 7

read start 2

read start 8

read end 8

read end 1

read start 6

read end 6

read start 5

read end 5

read end 3

read end 2

write start 0

write start end 0

write start 2

write start end 2

write start 3

write start end 3

write start 4

write start end 4

write start 5

write start end 5

write start 6

write start end 6

write start 7

write start end 7

write start 8

write start end 8

write start 9

write start end 9

[1 0 2 3 4 5 6 7 8 9]

从结果我们可以看出,开始的时候读线程占用读锁,并且多个线程可以同时开始读操作,但是写操作只能单个进行。

3)使用条件变量实现线程同步

go语言提供了条件变量sync.Cond,sync.Cond方法如下:

Wait,Signal,Broadcast。
Wait添加一个计数,也就是添加一个阻塞的goroutine。
Signal解除一个goroutine的阻塞,计数减一。
Broadcast接触所有wait goroutine的阻塞。

代码如下:

func printIntValue(value int, cond *sync.Cond) {
	cond.L.Lock()
	if value < 5 {
		//value小于5时,进入等待状态
		cond.Wait()
	}
	//大于5的正常输出
	fmt.Println(value)
	cond.L.Unlock()
}

func main() {

	//条件等待
	mutex := sync.Mutex{}
	//使用锁创建一个条件等待
	cond := sync.NewCond(&mutex)

	for i := 0; i < 10; i++ {
		go printIntValue(i, cond)
	}

	time.Sleep(time.Second * 1)
	cond.Signal()//解除一个阻塞
	time.Sleep(time.Second * 1)
	cond.Broadcast()//解除全部阻塞
	time.Sleep(time.Second * 1)
}

运行后先输出满足条件的值:5 6 7 8 9

解除一个阻塞,输出0,解除全部阻塞,输出1 2 3 4

go语言多线程支持全局唯一性操作,即一个只允许goruntine调用一次,重复调用无效。

go语言还支持sync.WaitGroup实现多个线程同步的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go语言多线程与并发编程 的相关文章

  • SD标准以及规范

    SD标准及规范 SD应用 SD标准让制造商能生产高性能之产品来提升数百万计消费者的体验 xff0c 包含听音乐 录制视频 摄影 数据储存以及使用移动电话 身为一个产业的标准 xff0c SD标准被用于行动存储产业的多个市场领域中 xff0c
  • 《视觉SLAM十四讲》学习笔记-状态估计问题

    最大后验与似然 经典slam模型可表示为 xff1a x k 61 f x k 1 u k 43 w k z k j 61 h y j x k 43 v
  • 机器人学领域的顶级期刊和会议

    印象中 xff0c 机器人学涉及机械 控制 计算机和电子等领域 xff0c 十足的交叉学科 xff0c 所以涉及到的概念和技术也非常多 因工作关系 xff0c 看了三周的SLAM入门 xff0c 用的是高翔的 视觉SLAM十四讲 这本教材
  • SQL解析过程

    转载自 xff1a http blog aliyun com 733 简介 SQL任务是ODPS中使用最频繁的一类作业 大部分用户开始使用ODPS时要做的第一件事情就是学习怎么写ODPS的SQL ODPS SQL是一种非常灵活的语言 兼容大
  • C++中类所占的内存大小以及成员函数的存储位置

    类所占内存的大小是由成员变量 xff08 静态变量除外 xff09 决定的 xff0c 虚函数指针和虚基类指针也属于数据部分 xff0c 成员函数是不计算在内的 因为在编译器处理后 xff0c 成员变量和成员函数是分离的 成员函数还是以一般
  • 用于异常检测的深度神经网络模型融合

    用于异常检测的深度神经网络模型融合 在当今的数字时代 xff0c 网络安全至关重要 xff0c 因为全球数十亿台计算机通过网络连接 近年来 xff0c 网络攻击的数量大幅增加 因此 xff0c 网络威胁检测旨在通过观察一段时间内的流量数据来
  • STM32CubeIDE构建通用freertos项目(一)

    感慨 本人大约三四年没有碰单片机了 xff0c 遥想当年我还是用的keil工具 有幸以援助的身份介入公司的嵌入式项目 xff0c 结合自身经验讲讲 工作是一个长期的过程 xff0c 开头不注意则会产生蝴蝶效应 xff0c 导致接下来的工作一
  • CMake中CMakeLists文件的编写以及变量打印

    最近在学习PCL xff0c 在Win10下使用VS编写PCL程序 xff0c 配置环境时经常出错 xff0c 踩坑记录详见 xff1a Win10 43 VS2017 43 PCL 1 8 1软件安装 踩坑记录 看到 点云库PCL从入门到
  • 如何离线安装python包

    在我们的日常使用python的过程中 xff0c 通常是通过联网安装相关的依赖包 xff0c 但是有时候会有一些情况是没有网络的 xff0c 但我们又需要安装python的各种包 而包的依赖导致我们很难一个一个地从pypi网站下载whl文件
  • mavros 环境配置注意事项[Resource not found: px4 ROS path ]

    背景 我最近开始使用mavros xff0c 按照官网的教程进行安装 采用二进制的方式安装 xff0c 一切进行的很顺利 xff0c 接下来我就按照PX4官网的去执行一个让无人机自动起飞的例子 xff0c 完全按照官网的代码和配置到最后一步
  • rosdep init ERROR: cannot download default sources list... 解决方法

    问题描述 如标题所示 xff0c 当我们安装好ROS后 xff0c 想要用rosdep初始化时 xff0c 会遇到ERROR cannot download default sources list from https raw githu
  • Linux 运行命令时修改.bashrc并结束命令时恢复原样

    问题来源 我有一个bash程序 xff0c 想要在执行该程序的时候修 bashrc xff0c 然后更新一些环境变量 xff0c 并在结束 ctrl 43 c 的时候再把程序恢复原样 操作如下 xff1a echo 命令把想要增加的内容写入
  • 数字信号处理实验(一)

    实验目的 本次实验目的为 xff1a 在matlab环境下产生几种基本的数字信号 xff0c 并对这些基本的信号进行运算和变换 xff0c 同时利用程序结果对采样定理进行验证 xff0c 深刻理解采样定理 通过自己录制音频信号并对不同的音频
  • 数字图像处理实验(二)

    实验目的 实验一 xff1a 图像增强 了解图像增强的目的及意义 xff0c 加深对图像增强的感性认知 1 掌握直接灰度变换的图像增强方法 2 掌握灰度直方图的概念及其计算方法 3 掌握直方图均衡化合直方图规定化得计算过程 实验二 xff1
  • 信息论实验-信源编码2(Lz编码和算数编码的C++实现)

    上一篇文章给出了Huffman编码和Shannon Fano编码的编码原理以及C 43 43 的程序 xff0c 程序可以用来实现给任意类型的文件进行无损压缩 xff0c 缺点是比较耗时 xff0c 不能作为正常的通用压缩软件来使用 xff
  • 信息论实验-二元对称信道仿真(C++实现)

    二元对称信道模拟器 实验目的 加深理解二进制对称信道的工作原理 xff0c 掌握通过高级编程语言生成伪随机数的方法 允许使用编程语言 xff1a C xff0c C 43 43 等 实验要求 输入 xff1a BSC信道的错误概率 xff0
  • OpenCV 安装必看

    怎样安装OpenCV套件呢 xff1f 想要使用opencv的同学一定是刚刚接触到图像处理 xff0c 需要做一些实验 xff0c 听说OpenCV很好用 xff0c 所以就开始查找各种资料学习OpenCV但是 xff0c 谁告诉你们它很好
  • make: *** No rule to make target `menuconfig'. Stop.问题解决方案-Linux(3)

    前言 本问题是我在编译更新内核时所遇到的 xff0c 已经解决 问题描述 在编译内核时 xff0c 运行make menuconfig 时出现 xff0c 截图如下 这个是因为没有找到要配置的文件 解决方案 进入解压得到的Linux原文件夹
  • 深入理解AlexNet网络

    AlexNet 论文 xff1a ImageNet Classification with Deep Convolutional Neural Networks 第一个典型的CNN是LeNet5网络结构 xff0c 但是第一个引起大家注意的

随机推荐

  • Ubuntu 18.04 网络配置

    坑爹的网络配置 ubuntu 18 04的网络配置的方式相较于原来的版本有了很大的改动 xff0c 并且server版的和Desktop 版本的是不一样的 Server版本 新的版本采用了netplan 管理网络 xff0c 在命令行中配置
  • PCA原理

    PCA 各位 xff0c 久违了 xff5e 什么是PCA xff1f 什么是PCA呢 xff1f 这是一个问题 xff0c 什么样的问题 xff1f 简单而又复杂的问题 xff0c 简单是因为百度一下就会出现一大堆的解释 xff0c 复杂
  • SRAM驱动开发实例

    一 我写博客的原因 xff0c 应该说是有两点吧 xff08 1 xff09 一点是对阶段性工作的总结 xff0c 虽说技术创新 xff0c 技术创新 xff0c 但在创新之前有一个技术积累的过程 xff0c 写博客 xff0c 便于总结
  • 互补滤波器

    互补滤波器 从 RC 电路 到 数字滤波器 参考 xff1a wikiPedia by luoshi006 原理 低通滤波器 一阶低通滤波器 传递函数 常见的 RC 电路构成的一阶低通滤波器的输入 U 输出 Y 关系如下 xff1a Y U
  • mahony 互补滤波器

    by luoshi006 上接 互补滤波器 xff0c 继续学习互补滤波 参考 xff1a Keeping a Good Attitude A Quaternion Based Orientation Filter for IMUs and
  • PX4 - position_estimator_inav

    by luoshi006 参考 xff1a 1 http dev px4 io advanced switching state estimators html 2 http blog sina com cn s blog 8fe4f2f4
  • PX4-terrain_estimator

    by luoshi006 参考 xff1a https github com PX4 Firmware blob master src lib terrain estimation terrain estimator h PX4 位置估计中
  • 已归档博文收纳

    by luoshi006 欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a L类 Learning GitHub Qt5 5 1 43 OpenCV2 4 10 环境配置PX4 开发环境 xff08 Ubuntu14
  • linux信号量

    1 信号量数据结构 union semun int val 整形变量 信号量初值 struct semid ds buf semid ds结构指针 unsigned short array 数组类型 struct seminfo buf 信
  • 单片机系统中的一种文字平滑(抗锯齿)显示方法

    这是之前一个项目中已实现的方法 xff0c 现在整理后拿出来分享 单片机系统中文字显示部分一般用的是用取模软件取出字模后输出到屏幕上显示 xff0c 这种方式显示的字体有锯齿 xff0c 在DPI低的显示屏上锯齿效果非常明显 对于单色屏来说
  • DIY个人智能家庭网关—— 路由器篇之刷机

    先选择合适的路由器型号 xff0c 要求有两个 xff0c 一 xff1a 有USB口 xff0c 二 xff1a 可以刷openwrt系统 xff0c 我的首选是newifi mini xff0c 比较喜欢折腾的可以刷openwrt官方固
  • 类中的域

    转自http book 51cto com art 201207 350797 htm 类的对象也称为这个类的实例 当创建一个对象时 xff0c 对象包含在类定义中包括的所有域 但是 xff0c 类定义中的域并不总是一样 一共有两种 有一种
  • Postman 插件介绍

    Postman介绍 Postman是google开发的一款功能强大的网页调试与发送网页HTTP请求 xff0c 并能运行测试用例的的Chrome插件 其主要功能包括 xff1a 模拟各种HTTP requests 从常用的 GET POST
  • 【2022阿里灵犀互娱】游戏测开笔试AC_Code

    测开笔试 xff0c 90分钟 xff0c 3道编程题 43 八股 xff0c 第二题输出格式模拟题 xff0c 就不贴了 T1 进制转换 题意 有一个数 xff0c 可能是2 xff5e 16进制的其中之一 xff0c 算出所有可能的结果
  • error while loading shared libraries: libopencv_imgcodecs.so.3.4

    最近给电脑重新安装了opencv3 4 10 xff0c 但是跑工程时却出现这个问题 xff0c 网上百度了一堆 xff0c 发现应该是库设置的问题 xff0c 但是到底是哪里出了问题 xff0c 怎么设置 xff0c 我根据我自己的实际情
  • 六、geotrellis按时间序列存储至hbase

    实现代码如下 xff1a import org apache camel scala dsl builder RouteBuilderSupport import geotrellis raster import geotrellis pr
  • 支持向量机svm及python测试

    from sklearn svm import SVR SVC from sklearn datasets import load boston from sklearn datasets import load wine from skl
  • mlp神经网络及python测试

    关于线性模型 xff1a y 39 61 w 0 x 0 43 w 0 x 0 43 43 w p x p 43 b 其中 xff0c y 39 表示对y的估算值 xff0c x 0 到x p 是样本特征值 w表示每个特征值的权重 xff0
  • linux 安装gdal(含hdf)记录

    一 准备工作 经实践 xff0c 在其他用户下存在某种问题 xff0c 可能是对Centos系统上不熟悉 xff0c 无法解决 xff0c 需在root用户下安装 xff09 xff1a su root 安装编译环境 sudo yum in
  • go语言多线程与并发编程

    go语言并发编程 在了解go语言的并发编程之前 xff0c 我们必须先了解并发和并行的概念 我们知道 xff0c 当启动一个应用的时候实际上是启动了一个进程 xff0c 通过该进程实现资源的调度和分配 xff0c 并且多个进程之间是相互隔离