go语言学习-并发编程(并发并行、线程协程、通道channel)

1、 概念

1.1 并发和并行

  • 并发:具有处理多个任务的能力 (是一个处理器在处理任务),cpu处理不同的任务会有时间错位,比如有A B 两个任务,某一时间段内在处理A任务,这时A任务需要停止运行一段时间,那么会切换到处理B任务,B任务停止运行,在切换处理A任务,只不过CPU处理快,看起来是同时处理多个任务。
  • 并行:同时执行多个任务的能力 (多个处理器) 。 比如3 个任务同时创建,cpu是3核的,那么3个处理器同时处理就是并行。

1.2 线程和协程

  • 线程(Thread)是计算机操作系统中程序运行时最小的执行单元,,是CPU调度的基本单元。一个线程可以理解为一个任务,都具有一个独立的控制流。(python和java一般会使用,一个线程占用内存8MB)
    在多线程编程中,可以将不同的任务或操作封装在不同的线程中,然后并发地执行这些任务,从而提高程序的性能和响应速度。
  • 协程(Coroutine)是一种轻量级的线程,也被称为用户态线程。与操作系统中的线程不同,协程由程序员自己管理调度。协程可以使用更少的资源,更快的创建和销毁,协程之间可以通过通道 (Channel) 进行通信和同步,避免了传统线程中锁等同步机制的开销和复杂性。(go使用的是协程,一个协程占用内存2KB)

2、go实现并发处理(GoRoutine)

GoRoutine实现: 只需要语句前添加go关键字即可。

注意: 主程序创建了协程以后,并不会等待所有的协程执行成功,需要主程序等待协程处理完成之后再去退出主程序

package main

import (
	"fmt"
	"time"
)

// 做包子的函数
func makeBuns(filling string) {
	startTime := time.Now()
	fmt.Printf("%s馅开始的时间:%s", filling, startTime)
	fmt.Printf("开始做%s馅的包子\n", filling)
	// 1. 剁馅
	fmt.Printf("开始剁%s馅...\n", filling)
	// 2. 擀皮
	fmt.Println("开始擀皮...")
	time.Sleep(time.Second)
	// 3. 包包子
	fmt.Printf("开始包%s馅的包子...\n", filling)
	// 4. 蒸
	fmt.Printf("开始蒸%s馅的包子...\n", filling)
	cost := time.Since(startTime)
	fmt.Printf("%s馅共耗费的时间:%s", filling, cost)
}

func main() {
	// go: goroutine并发处理,go 代码语句
	// 协程:2KB,线程:8MB
	// 不需要关心协程的底层逻辑、内存管理以及垃圾回收
	fillings := []string{"韭菜", "鸡蛋", "西葫芦"}
	for _, v := range fillings {
		// 包
		go makeBuns(v) // 它的时间会缩短吗?
		// 主程序创建了协程以后,并不会等待所有的协程执行成功,需要主程序等待协程处理完成之后再去退出主程序
	}
	time.Sleep(time.Second * 3) // 开发程序时不能用time.Sleep去等待!!!!
}

通过运行函数可以发现基本是同一时间执行的。
在这里插入图片描述

3、通道

3.1 基本语法

	// go的通道,也是一种类型。channel1 := make(chan 类型)就可以声明一个通道。这个通道可以理解为类似于共享文件的存储。
	//如果通道内没有数据,就会一直在此处阻塞,直到取到数据为止
	//通道打开之后需要通过closer关闭
	buns := make(chan string) //该通道只有一个位置
	buns := make(chan string,5) //该通道有五个位置
	channel <- "数据1" //发送数据
	data := <- channel //取数据
	defer close(buns) //等程序执行完后,关闭通道

3.2 阻塞原理

buns := make(chan string)
 
//1、此时的通道默认只有一个位置,比如传入了值,那么下一个值传需要等待这个值被拿走,才可以有另外的一个数据进入。
//2、如果这个通道里面没有数据,也会被阻塞,等待有数据后,才可以继续往下走流程。

3.3 实践

package main

import (
	"fmt"
	"time"
)

// 做包子的函数
func makeBuns(filling string, buns chan string) {
	fmt.Printf("开始做%s馅的包子\n", filling)
	// 1. 剁馅
	fmt.Printf("开始剁%s馅...\n", filling)
	// 2. 擀皮
	fmt.Println("开始擀皮...")
	time.Sleep(time.Second)
	// 3. 包包子
	fmt.Printf("开始包%s馅的包子...\n", filling)
	// 4. 蒸
	fmt.Printf("开始蒸%s馅的包子...\n", filling)
	// 5. 蒸好了
	time.Sleep(time.Second * 1)
	fmt.Printf("%s馅包子已经蒸好了,可以上菜了,蒸好的时间是:%s\n", filling, time.Now())
	// 6. 上菜
	// 在这个位置把蒸好的包子放到通道内
	buns <- filling
	fmt.Printf("%s馅包子已经放在了上菜区,放置时间是:%s\n", filling, time.Now())
}

// 上菜的函数
// func waiter(buns chan string) {
// 	// 把蒸好的包子拿出来去上菜
// 	bun := <-buns
// 	fmt.Printf("上菜:%s, 上菜时间:%s\n", bun, time.Now())
// }


func main() {
	// go的通道,也是一种类型。channel1 := make(chan 类型)就可以声明一个通道
	// 发送数据: channel1 <- "数据1"
	// 取数据: data1 := <- channel1
	// 定义一个蒸好的包子的通道
	buns := make(chan string, 5)
	defer close(buns) //通道打开之后需要关闭
	// go: goroutine并发处理,go 代码语句
	// 协程:2KB,线程:8MB
	// 不需要关心协程的底层逻辑、内存管理以及垃圾回收
	// 做包子
	//
	fillings := []string{"韭菜", "鸡蛋", "西葫芦"}
	startTime := time.Now()
	for _, v := range fillings {
		// 包
		go makeBuns(v, buns) 
		// 主程序创建了协程以后,并不会等待所有的协程执行成功,需要主程序等待协程处理完成之后再去退出主程序
	}
	//取包子
	for i := 0; i < len(fillings); i++ {
		// 在这里直接去包子
		// 把蒸好的包子拿出来去上菜
		time.Sleep(3 * time.Second)
		bun := <-buns // 如果通道内没有数据,就会一直在此处阻塞,直到取到数据为止
		fmt.Printf("上菜:%s, 上菜时间:%s\n", bun, time.Now())
	}
	cost := time.Since(startTime)
	fmt.Println("共耗费的时间:", cost)
	// time.Sleep(time.Second * 3) // 开发程序时不能用time.Sleep去等待!!!!

}

3.4 Select处理多个channel通道

比如一个饭店多个菜系的师傅,然后相关菜系会放到对应的存放位置,然后服务员到对一个的位置取菜。

//select 基础语法

select {
	case dish := <-chef1:
		fmt.Println("厨师chef1已经做好了:", dish)
	case dish := <-chef2:
		fmt.Println("厨师chef2已经做好了:", dish)
	case <-time.After(time.Second * 3): //对通道进行超市时间处理时候常用time.After
		fmt.Println("你们做饭太慢了,我不吃了,拜拜")
	}
// select包含多个case语句,每个case语句用于接收某一个通道的数据,当某一个通道有了数据之后,就会执行对应的case语句。多个case语句,同时都包含了数据,不确定,会随机执行某一个
//练习
package main

import (
	"fmt"
	"time"
)

// 做菜的函数
func cookDish(chef, dishName string, c chan string) {
	fmt.Printf("厨师:%s正在做:%s\n", chef, dishName)
	time.Sleep(time.Second * 5)
	// 做好的菜放进通道内
	c <- dishName
}

func main() {
	// 定义两个channel去存放不同的数据
	chef1 := make(chan string)
	chef2 := make(chan string)
	go cookDish("chef1", "烤鸭", chef1)
	go cookDish("chef2", "佛跳墙", chef2)

	// 等待获取数据
	// select包含多个case语句,每个case语句用于接收某一个通道的数据
	// 当某一个通道有了数据之后,就会执行对应的case语句
	// 多个case语句,同时都包含了数据,不确定,会随机执行某一个
	select {
	case dish := <-chef1:
		fmt.Println("厨师chef1已经做好了:", dish)
	case dish := <-chef2:
		fmt.Println("厨师chef2已经做好了:", dish)
	case <-time.After(time.Second * 3):
		fmt.Println("你们做饭太慢了,我不吃了,拜拜")
	}
	close(chef1)
	close(chef2)
}

3.5 监听通道的退出信号

//通过定义Bool类型的通道,可以快速监听退出信号或者监听错误信息
package main

import (
	"fmt"
	"time"
)

//定义拧螺丝的函数
func screw(c chan int) {
	i := 1
	for {
		fmt.Printf("正在拧第%d个螺丝\n", i)
		c <- i
		i++
		time.Sleep(time.Second)
	}
}

func main() {
	// 定义一个拧螺丝的通道
	screwChan := make(chan int, 100)
	// 定义一个关闭通道的通道
	stop := make(chan bool)
	go screw(screwChan)
	go func() {
		time.Sleep(10 * time.Second)
		fmt.Println("下班了,走啦,不干了")
		stop <- true
	}()
	for {
		select {
		case <-stop:
			// 说明我们的倒计时已经到了,并且往stop这个通道内发送了true的数据
			fmt.Println("下班了,不弄了")
			return
		case s := <-screwChan:
			fmt.Printf("第%d个螺丝已完成\n", s)
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DockerFile常用保留字指令及知识点合集

目录 DockerFile加深理解&#xff1a; DockerFile常用保留字指令 保留字&#xff1a; RUN&#xff1a;容器构建时需要运行的命令 COPY&#xff1a;类似ADD&#xff0c;拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 …

【动态规划】LeetCode-面试题 17.16. 按摩师

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

vs 安装 qt qt扩展

1 安装qt 社区版 免费 Download Qt OSS: Get Qt Online Installer 2 vs安装 qt vs tools 3 vs添加 qt添加 bin/cmake.exe 路径 3.1 扩展 -> qt versions 3.2

【STM32】STM32学习笔记-新建工程(04)

00. 目录 文章目录 00. 目录01. 创建STM32工程02. STM32工程编译和下载03. LED测试04. 型号分类及缩写05. 工程结构06. 附录 01. 创建STM32工程 【STM32】STM32F103C8T6 创建工程模版详解(固件库) 02. STM32工程编译和下载 2.1 选择下载器位ST-Link Debugger 2.2 勾选上电…

04. 函数

目录 1、前言 2、Python中的函数 2.1、内置函数 2.2、自定义函数 2.3、函数调用 3、函数的参数 3.1、形参和实参 3.2、位置参数&#xff08;Positional Arguments&#xff09; 3.3、默认参数&#xff08;Default Arguments&#xff09;&#xff1a; 3.4、关键字参数&a…

如何为C#WinFrom编译的.exe添加个性化图标

1、在VS中点击菜单栏上的“项目”,找到最下面的属性&#xff0c;单击进去 2、加载自定义的.ico文件&#xff0c;如果没有此格式的文件可以使用此网站去转换&#xff1a;图标制作大师 - 轻松制作网站favicon图标 3、重新编译文件即可

【【水 MicroBlaze 最后的介绍和使用】】

水 MicroBlaze 最后的介绍和使用 我对MicroBlaze 已经有了一个普遍的理解 了 现在我将看的两个 一个是 AXI4接口的 DDR读写实验 还有一个是 AXI DMA 环路实验 虽然是 水文 但是 也许能从中 得到一些收获 第一个是 AXI DDR 读写实验 Xilinx 从 Spartan-6 和 Virtex-6 系列开始…

SSM框架(六):SpringBoot技术及整合SSM

文章目录 一、概述1.1 简介1.2 起步依赖1.3 入门案例1.4 快速启动 二、基础配置2.1 三种配置文件方式2.2 yaml文件格式2.3 yaml读取数据方式&#xff08;3种&#xff09; 三、多环境开发3.1 yml文件-多环境开发3.2 properties文件-多环境开发3.3 多环境命令行启动参数设置3.4 多…

【数值计算方法(黄明游)】函数插值与曲线拟合(一):Lagrange插值【理论到程序】

​ 文章目录 一、近似表达方式1. 插值&#xff08;Interpolation&#xff09;2. 拟合&#xff08;Fitting&#xff09;3. 投影&#xff08;Projection&#xff09; 二、Lagrange插值1. 天书1. 人话拉格朗日插值方法a. 线性插值&#xff08;n1&#xff09;基本思想线性插值与线…

解决uview中uni-popup弹出层不能设置高度问题

开发场景&#xff1a;点击条件筛选按钮&#xff0c;在弹出的popup框中让用户选择条件进行筛选 但是在iphone12/13pro展示是正常&#xff0c;但是切换至其他手机型号就填充满了整个屏幕&#xff0c;需要给这个弹窗设置一个固定的高度 iphone12/13pro与其他型号手机对比 一开始…

智能优化算法应用:基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于海洋捕食者算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.海洋捕食者算法4.实验参数设定5.算法结果…

工业机器视觉megauging(向光有光)使用说明书(四,轻量级的visionpro)

第三个相机的添加&#xff0c;突然发现需要补充一下&#xff1a; 第一步&#xff0c;假定你对c#编程懂一点&#xff0c;我们添加了一个页面“相机三”在tabcontrol1&#xff1a; 第二步&#xff0c;添加dll到工具箱&#xff1a; 第三步&#xff0c;点击‘浏览’&#xff0c;找…

Web前端JS如何获取 Video/Audio 视音频声道(左右声道|多声道)、视音频轨道、音频流数据

写在前面&#xff1a; 根据Web项目开发需求&#xff0c;需要在H5页面中&#xff0c;通过点击视频列表页中的任意视频进入视频详情页&#xff0c;然后根据视频的链接地址&#xff0c;主要是 .mp4 文件格式&#xff0c;在进行播放时实时的显示该视频的音频轨道情况&#xff0c;并…

Fiddler抓包工具之Fiddler+willow插件应用

安装Fiddler的安装包地址&#xff1a;fillderwillow 解压后安装fiddler4和willow1.4.*版本。 安装成功后&#xff0c;启动fiddler后会出现willow插件按钮&#xff1a; 说明安装成功。 重定向 willow重定向 进入willow界面后&#xff0c;通过右键->Add Project ->Add Ru…

canvas基础:fillStyle 和strokeStyle示例

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 上色…

Spring Task 超详解版

目录 一、定时任务的理解 二、入门案例 三、Cron表达式 四、Cron实战案例 五、多线程案例 一、定时任务的理解 定时任务即系统在特定时间执行一段代码&#xff0c;它的场景应用非常广泛&#xff1a; 购买游戏的月卡会员后&#xff0c;系统每天给会员发放游戏资源。管理系…

基于姿态估计的3D动画生成

在本文中&#xff0c;我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作&#xff0c;并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

EasyMetagenome易宏基因组——简单易用的宏基因组分析流程-来自刘永鑫团队的秘密武器

原仓库地址如下&#xff0c;github有时候无法访问&#xff0c;等一段时间再试就行&#xff1a; YongxinLiu/EasyMetagenome: Easy Metagenome Pipeline (github.com) 相关文章&#xff0c;看文章更清晰这个可干啥&#xff1a; EasyAmplicon: An easy‐to‐use, open‐source…

JAVA高级-1

常用API 第一章 API 产品说明书 第二章 Scanner类&#xff08;输入&#xff09; 功能&#xff1a;获取键盘输入 package day7_12.demo01_Scanner;import java.util.Scanner; //1、导包 /* 功能&#xff1a;获取键盘输入引用类型一般使用步骤1、导包&#xff1a;impo…

深入了解汉字转拼音转换工具:原理与应用

一、引言 汉字作为世界上最古老、最具象形意的文字之一&#xff0c;承载了数千年的历史文明。然而&#xff0c;在现代信息技术环境下&#xff0c;汉字的输入、输出和检索等方面存在一定的局限性。拼音作为汉字的一种音标表达方式&#xff0c;能够有效地解决这些问题。本文将为…