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

news2024/11/24 14:20:10

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)
		}
	}
}

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

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

相关文章

基于redisson实现发布订阅(多服务间用避坑)

前言 今天要分享的是基于Redisson实现信息发布与订阅&#xff08;以前分享过直接基于redis的实现&#xff09;&#xff0c;如果你是在多服务间基于redisson做信息传递&#xff0c;并且有服务压根就收不到信息&#xff0c;那你一定要看完。 今天其实重点是避坑&#xff0…

MySQL 对null 值的特殊处理

需求 需要将不再有效范围内的所有数据都删除&#xff0c;所以用not in (有效list)去实现&#xff0c;但是发现库里&#xff0c;这一列为null的值并没有删除&#xff0c;突然想到是不是跟 anull 不能生效一样&#xff0c;not in 对null不生效&#xff0c;也需要特殊处理。 解决 …

$sformat在仿真中打印文本名的使用

在仿真中&#xff0c;定义队列&#xff0c;使用任务进行函数传递&#xff0c;并传递文件名&#xff0c;传递队列&#xff0c;进行打印 $sformat(filename, “./data_log/%0d_%0d_%0d_0.txt”, f_num, lane_num,dt); 使用此函数可以自定义字符串&#xff0c;在仿真的时候进行文件…

Python基础(二、必备知识,不用背,用用就会了~)

一、基础知识 1.标识符 在Python中&#xff0c;标识符是用来标识变量、函数、类、模块或其他对象的名称。一个有效的标识符由字母、数字和下划线组成&#xff0c;且不能以数字开头。Python是区分大小写的&#xff0c;因此myVariable和myvariable被视为不同的标识符。 下面是…

Redis数据已经删除了,为什么内存占用还是很高?

Redis数据已经删除了&#xff0c;为什么内存占用还是很高&#xff1f; Redis做了数据删除操作&#xff0c;为什么使用top命令时&#xff0c;还是显示Redis占了很多内存&#xff1f; 没做相关功课的人觉得这个问题有问题&#xff0c;删了数据还说占着内存&#xff0c;面试官不…

QQ2023备份

需要修改的路径&#xff08;共3处&#xff09; 这三处路径中&#xff0c;只有一处是需要修改的 QQPC端-主菜单-设置-基本设置-文件管理 点击上面的“”自定义“”&#xff0c;然后修改路径即可 修改路径后提示 然后等一会才会关干净QQ的相关进程&#xff0c;关闭后才会有自动…

Windows的C盘爆掉了怎么办?

本文参考&#xff1a; C盘太满怎么办&#xff1f;亲测8种好用方法&#xff01; 如果C盘的分区爆掉了&#xff0c;变红色了&#xff0c;是时候该处理这个问题了&#xff0c;解决你的C盘焦虑&#xff01; 第一招&#xff1a;删除C盘文件 首先你会想到清理C盘里面的文件&#x…

Flink入门之核心概念(三)

任务槽 TaskSlots: 任务槽&#xff0c;是TaskManager提供的用于执行Task的资源&#xff08;CPU 内存&#xff09; TaskManager提供的TaskSlots的个数&#xff1a;主要由Taskmanager所在机器的CPU核心数来决定&#xff0c;不能超过CPU的最大核心数 1.可以在flink/conf/flink-c…

【C++】map/multimap/set/multiset的经典oj例题 [ 盘点&全面解析 ] (28)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.前K个高频单词【mutiset】二.左右符…

云服务器Centos中安装Docker

云服务器Centos中安装Docker 1 简介DockerCentosCentos和Ubuntu区别 2 安装3 测试hello-world的镜像测试 1 简介 Docker Docker是一个开源的应用容器引擎&#xff0c;利用操作系统本身已有的机制和特性&#xff0c;可以实现远超传统虚拟机的轻量级虚拟化。它支持将软件编译成…

虚拟机-桥接模式连接

文章目录 1.查看宿主机再用的IP信息2.桥接模式-虚拟机设置VMware设置虚拟机设置重启网络服务 1.查看宿主机再用的IP信息 ipconfig /all 注&#xff1a; 在虚拟机中要设置同网段的ip设置同一个子网掩码设置同一个网关设置同一个DNS服务器 2.桥接模式-虚拟机设置 VMware设置 虚…

整数在内存中的存储

整数和浮点数在内存中的存储方式是不一样的&#xff0c;今天&#xff0c;我们来具体学习一下 文章目录 整数在内存中的存储浮点数在内存中的存储 整数在内存中的存储 我们在之前就已经了解过了整数有原码&#xff0c;反码&#xff0c;补码的形式&#xff0c;这三种方式都是二进…

cache教程 2.单机并发缓存

0.对原教程的一些见解 个人认为原教程中两点知识的引入不够友好。 首先是只读数据结构 ByteView 的引入使用是有点迷茫的&#xff0c;可能不能很好理解为什么需要ByteView。 第二是主体结构 Group的引入也疑惑。其实要是熟悉groupcache&#xff0c;那对结构Group的使用是清晰…

LangChain学习二:提示-实战

文章目录 上一节内容&#xff1a;LangChain学习一&#xff1a;模型-实战学习目标&#xff1a;提示词及提示词模板的运用学习内容一&#xff1a;什么是提示词&#xff1f;学习内容二&#xff1a;提示词模板2.1 聊天提示模板实战:首先需要声明和定义一个模板实战:把提示词模板放入…

分类预测 | GASF-CNN格拉姆角场-卷积神经网络的数据分类预测

分类预测 | GASF-CNN格拉姆角场-卷积神经网络的数据分类预测 目录 分类预测 | GASF-CNN格拉姆角场-卷积神经网络的数据分类预测分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.GASF-CNN格拉姆角场-卷积神经网络的数据分类预测&#xff08;完整源码和数据) 2.自…

Leetcode2477. 到达首都的最少油耗

Every day a Leetcode 题目来源&#xff1a;2477. 到达首都的最少油耗 解法1&#xff1a;贪心 深度优先搜索 题目等价于给出了一棵以节点 0 为根结点的树&#xff0c;并且初始树上的每一个节点上都有一个人&#xff0c;现在所有人都需要通过「车子」向结点 0 移动。 对于…

os.walk()遍历文件夹/文件

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

智能优化算法应用:基于人工蜂鸟算法无线传感器网络(WSN)覆盖优化 - 附代码

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

Qt槽函数不响应不执行的一种原因:ui提升导致重名

背景&#xff1a; 一个包含了组件提升的ui&#xff0c;有个按钮的槽函数就是不响应&#xff0c;于是找原因。 分析&#xff1a; 槽函数的对应一是通过connect函数绑定信号&#xff0c;二是on_XXX_signal的命名方式。界面上部件的槽函数通常是第二种。 我反复确认细节&#…

正则表达式:字符串处理的瑞士军刀

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…