go语言基础 -- goroutine与管道

news2024/11/15 21:35:27

go协程特点

一个线程上可以起多个goroutine,goroutine有以下特点:

  1. 有独立的栈空间
  2. 共享程序堆空间
  3. 调度由用户控制

下面是一个简单的协程使用案例:

package main
import(
    "fmt"
    "strconv"
)

func test() {
    for i := 0; i < 10; i++ {
        fmt.Println("test func:" + strconv.itoa(i))
        time.Sleep(time.second)
    }
}

func main() {
    // 开启一个协程
    go test()
    for i := 0; i <= 10; i++ {
        fmt.Println("main func:" + strconv.itoa(i)
        time.Sleep(time.second)
    }
}

如果主线程退出了,协程即时还没执行完也会退出,因此我们通常还需要加一些同步机制;

协程的MPG调度模型

M是线程,P是协程的上下文,G是协程
在这里插入图片描述
1、假设当前M0正在执行G0协程,如果G0协程阻塞读文件或网络数据时,这时系统会取出M1线程执行;
2、取出M1线程后,系统会将执行M1挂的三个协程执行,这时M0仍在执行阻塞操作中,等G0不再阻塞之后,M0又会被放到空闲主线程继续执行,G0会被唤醒

golang设置运行cpu数量

我们可以通过runtime包的函数来设置cpu数,go1.8之后默认让程序运行在多个cpu上,1.8之前需要人工设置

func main() {
    // 获取cpu数量
    cpu_num := runtime.RuntimeCPU()
    // 设置使用的cpu数量
    runtime.GOMAXPROCS(cpu_num - 1)
}

goroutine资源竞争及解决方案

多个goroutine之间操作同一个数据时,就会出现资源竞争问题,go语言在编译程序时也提供了一个-race参数来帮助开发者判断是否存在资源竞争。有两种方法来解决资源竞争:
(1)对竞争资源加锁;使用sync包里面的同步函数,不推荐使用

// 定义一个互斥锁变量
lock sync.Mutex
lock.Lock()
lock.Unlock()

(2)使用管道(推荐);

管道(channel)的基本介绍

管道本身就是一个先入先出的队列,但它是一个线程安全的队列,同时有类型,string类型的channel只能存储string类型的数据,虽然我们也可以用interface来存储多类型,但用的时候也需要用类型断言判断类型后再使用,声明管道的方式如下:
var 管道名 chan 数据类型

var Intchan chan int
var Mapchan chan map[string]int
var Personchan chan Person

管道是引用类型,使用前必须用make开辟内存初始化,管道的使用简单案例如下:

func main() {
    // 管道创建与初始化
    var IntChan chan int
    IntChan = make(chan int, 3)
    // 管道写入数据
    IntChan<- 5
    num := 100
    IntChan<- num
    // 从管道中读取数据
    out_num := <-IntChan
}

需要注意的是,channel中的数据如果已经取完了,再取会报错,报死锁,如果channel满了,就不能再放入了,再放也会报错

空接口作为管道数据类型

如果想让管道中存放多种类型的数据,需要将管道的数据类型声明为空接口下面是一个案例

type Cat struct {
    Name : string
    Age : int
}
func main() {
    // 定义一个空接口数据类型的管道
    var allchan chan interface{}
    allcha := make(char interface{}, 3)
    allchan<- 10
    allchan<- "Tom"
    cat := Cat{"xiaomao", 1}
    allchan<- cat
    // 推出前两个数据
    <-allchan
    <-allchan
    mycat := <-allchan
    // 这里打印出来cat类型和值是没问题的
    fmt.Printf("mycat type:%T, val:%v", mycat, mycat)
    // 在不使用类型断言之前,是不能直接把mycat当Cat类型使用的
    fmt.Printf("my cat name:%v", mycat.Name) // 会报错
    a := mycat.(Cat)
    fmt.Printf("mycat name:%v", a.Name)
}

channel的关闭和遍历

  • 使用内置函数close可以关闭channel,channel关闭后,可以读,但不能再继续往里面写
  • 管道只能使用for range的方式遍历,因为len会在减少
  • 在遍历时如果没有close管道,会出现deadlock(一直在等待数据写入)
close(IntChan)
for v := IntChan {
    fmt.Println("v:%v", v)
}

管道和goroutine的综合案例

package main
import(
    "fmt"
    
)

func writeData(intchan chan int) {
    for i := 1; i < 50; i++ {
        intchan<- i
    }
    close(intchan)
    
}

func ReadData(intchan chan int, exitchan chan bool)  {
    for {
        v, ok := <-intchan
        if !ok {
            break
        } 
        fmt.Printf()
    }
    exitchan<- true
    close(exitchan)
}

func main() {
    var intchan chan int = make(chan int, 50)
    exitchan := make(chan bool, 1)
    go writeData(intchan)
    go ReadData(intchan, exitchan)
    for v := exitchan {
        break
    }
}

需要注意的是,如果我们值往管道里面读,而没有往管道里面写的操作,那么就会报死锁错误,只要有写的操作,那么即时读写频率不一致,也不会死锁。

管道的使用细节

  1. 默认情况下,管道是双向管道,我们可人工指定管道为只读或只写
func main() {
    var writeOnlyChan chan<- int
    var readOnlyChan <-chan int
    writeOnlyChan = make(chan int, 3)
    readOnlyChan = make(chan int, 3)
}
// 除了上面的场景外,多数情况我们是在函数参数里面声明管道为只读或只写
func recv(ch <-chan int) {
    // ...
}
func send(ch chan<- int) {
    // ...
}
// 这时我们可以将一个双向管道作为参数传入,但在函数内部,双向管道将被限定只读或只写
var IntChan chan int
IntChan = make(chan int, 5)
recv(Intchan)
send(IntChan)
  1. 使用select可以解决从管道读数据阻塞的问题
    实际开发中不好确定什么时候关闭管道,在遍历管道时,如果不关闭管道,容易导致死锁(写操作没关,但没继续写了,读操作阻塞),我们可以用select来解决这个问题。
func main() {
    intChan := make(chan int, 3)
    strChan := make(chan string, 5)
    for i := 0; i < 3; i++ {
        intChan<- i
    }
    for j := 0; j < 5; j++ {
        strChan<- "str" + fmt.Sfrintf("%d", i)
    }
  
    for {
        select {
            case v := <-intChan :
                fmt.Printf("read from intchan;%v", v)
            case v := <-strChan :
                fmt.Printf("read from strChan:%v", v)
            default:
                return 
        }
    }
}
  1. 协程中的panic捕获,避免程序崩溃
    我们可以使用recover来捕获协程中的panic,避免影响其他协程
func test() {
    // 匿名函数捕获异常
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("test panic", err)
        }
    }()
    var tmpMap map[int]string
    map[0] = "make panic"
}
func main() {
    go test()
    
}

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

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

相关文章

18-任务管理常用API函数

任务挂起函数 void vTaskSuspend(TaskHandle_t xTaskToSuspend) void vTaskSuspendAll(void)——挂起所有任务 其实就是将调度器锁定&#xff0c;直白点挂起所有的任务&#xff0c;就是挂起任务调度器&#xff0c;调度器被挂起后&#xff0c;就不可以进行上下文的切换&#xf…

java中数组的定义与使用

Java中的数组跟c语言的数组几乎不一样&#xff0c;我们要区分对待。在之后你就能理解到我为什么说这句话了。 1.java中数组的创建与初始化 数组的创建 如下&#xff0c;皆为数组的创建。 double[] a; int[] b; 创建时的[]里面绝对不能填数字。 数组的初始化 主要分为动态…

【Python】一文带你了解如何获取 Python模块 安装路径

【Python】一文带你了解如何获取 Python模块 安装路径 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您的订阅…

MyBatis进阶篇

MyBatis MyBtias工具类参数入参参数是单个参数参数是多个参数入参是POJO对象入参是Map类型 自动主键增长#{}和${}两种获取参数方式结果映射动态SQLMyBatis多表查询MyBatis注解开发 MyBtias工具类 SessionUtils.java import org.apache.ibatis.io.Resources; import org.apache…

当HR问你:“什么事会让你有成就感”你该怎么回答?【文章底部添加进大学生就业交流群】

当HR问你“什么事会让你有成就感”时&#xff0c;你可以通过以下方式回答&#xff1a; 强调目标实现&#xff1a; 表达你在达成挑战性目标时感到的满足感。举例说明你在过去的工作或项目中如何设定并成功实现了目标。 强调对团队成功的贡献&#xff1a; 谈论你与团队合作取得成…

力扣每日一题 将标题首字母大写 模拟 String API

Problem: 2129. 将标题首字母大写 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String capitalizeTitle(String title)…

leetcode 25、k个一组翻转链表

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

C++程序设计-练手题集合【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下C程序设计中的练手题&#xff0c;以供大家期末复习和考研复习的时候使用。 C程序设计系列文章传送门&#xff1a; 第一章 面向对象基础 第四/五章 函数和类和对象 第六/七/八章 运算符重载/包含与继承/虚函数…

项目实战-tpshop商城项目

项目实战-tpshop商城项目 环境部署准备软件工具准备远程连接测试远程连接测试-查看虚拟机IP地址远程连接测试-检测本机与虚拟机是否连通远程连接测试-通过远程工具连接linux服务器 常见问题处理 环境部署项目技术架构介绍部署tpshop项目-tpshop验证数据库验证用户信息表熟悉商品…

API接口数据集接口pytorch api接口获取数据

API是应用程序的开发接口&#xff0c;在开发程序的时候&#xff0c;我们有些功能可能不需要从到到位去研发&#xff0c;我们可以拿现有的开发出来的功能模块来使用&#xff0c;而这个功能模块&#xff0c;就叫做库(libary)。比如说&#xff1a;要实现数据传输的安全&#xff0c…

这五款高性能骨传导耳机入手不后悔!附带骨传导耳机选购攻略!

随着健康生活的逐渐流行&#xff0c;越来越多的人开始注重运动和健身&#xff0c;在这一背景下&#xff0c;骨传导耳机作为当下最热门的健身装备&#xff0c;已成为市场上最受欢迎的产品之一&#xff0c;随着骨传导耳机的热度增高&#xff0c;与此同时也引发了一些不良商家和劣…

第八十天 WAF攻防-漏洞利用HPP污染分块传输垃圾数据

第80天 WAF攻防-漏洞利用&HPP污染&分块传输&垃圾数据 参考点&#xff1a; #将MySQL注入函数分为几类 拆分字符串函数&#xff1a;mid、1eft、1pad等 编码函数&#xff1a;ord、hex、a3ci等 运算函数&#xff1a;*/&^&#xff01;1 ike rlike reg等 空格替换部…

如何制作产品介绍二维码(一):编辑二维码内容

本篇主要以官方模板「产品详情介绍」为例&#xff0c;教大家如何从0开始制作单个产品介绍二维码&#xff0c;在掌握操作技巧后&#xff0c;你也能快速制作出一个效果不错的二维码。 该教程主要分以下4个部分&#xff1a; 编辑内容&#xff1a;指的是如何把图片、文件、音视频…

算法刷题day28

目录 引言一、截断数组二、双端队列三、日期统计 引言 这几道题是周赛里的几道题目&#xff0c;第一道题目我没用这种方法&#xff0c;但还是做出来了&#xff0c;用的一种比较特殊的思考方法&#xff0c;就是把每一个点都判断出来&#xff0c;不满足要求的就舍弃&#xff0c;…

ElasticSearchLinux安装和springboot整合的记录和遇到的问题

前面整合遇到的一些问题有的记录在下面了&#xff0c;有的当时忘了记录下来&#xff0c;希望下面的能帮到你们 1&#xff1a;Linux安装ES 下载安装&#xff1a; 参考文章&#xff1a;连接1 连接2 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch…

如何关闭微软的Edge浏览器右击提示的:“使用copilot重写“的提示?

最近在使用微软的edge浏览器写文档的时候&#xff0c;总是不小心右击鼠标&#xff0c;提示 有时候挺烦人的&#xff0c;那怎么关闭呢&#xff1f; 打开edge浏览器的设置 在设置中搜索AI&#xff0c;并关闭AI书写的选项就好了 这样就可以获得一个干净的界面了&#xff0c;不…

SPEL表达式及注入漏洞

SPEL,全称为Spring表达式语言&#xff0c;是一个由 Spring 框架提供的表达式语言。它是一种基于字符串的表达式语言&#xff0c;可以在运行时对对象进行查询和操作。 SpEL 支持在XML和注解配置中使用&#xff0c;它可以在Spring框架的各种组件中使用&#xff0c;如Spring MVC …

Visual grounding-视觉定位任务介绍

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

知识图谱技术综述

作者简介:徐增林(1980 − ),男,博士,教授,主要从事机器学习及其在社会网络分析、互联网、计算生物学、信息安全等方面的研究. 【摘要】 知识图谱技术是人工智能技术的重要组成部分,其建立的具有语义处理能力与开放互联能力的知识库,可在智能搜索、智能问答、个性化推…

Java中抽象类和接口有什么区别?

1、典型回答 接口和抽象类都是用来定义对象公共行为的&#xff0c;二者的主要区别有以下几点不同&#xff1a; 类型扩展不同&#xff1a;抽象类是单继承&#xff0c;而接口是多继承&#xff08;多实现&#xff09;方法/属性访问控制符不同&#xff1a;抽象类方法和属性使用访问…