goroutine学习

goroutine 为什么比java中的线程更加轻量?

两种并发机制在资源消耗、调度方式、创建和管理上有明显的差异:

  1. 堆栈大小:

    • Goroutine 的初始堆栈大小相对较小,通常在几 KB(2KB或4KB)左右,而且可以根据需要动态伸缩。这意味着在同样的内存条件下,可以创建更多的 goroutine。
    • Java 线程的堆栈大小通常较大,可能从几百 KB 到几 MB 不等,这取决于 JVM 的配置和操作系统。Java 线程的堆栈不会动态伸缩,因此每个线程都会占用更多的内存资源。
  2. 调度:

    • Goroutine 是由 Go 运行时(runtime)管理的,使用的是 M:N 调度模型(多个 goroutine 可以映射到多个或单个操作系统线程)。Go 运行时包含自己的调度器,它可以在用户态进行调度,避免了内核态与用户态之间的频繁切换,这使得创建和切换 goroutine 的成本比线程低得多。
    • Java 线程通常是直接映射到操作系统原生线程(1:1 模型),由操作系统内核进行调度。这意味着线程的创建和切换需要进行用户态和内核态之间的切换,这比用户态的调度更耗时。
  3. 创建和管理:

    • Goroutine 的创建和管理是由 Go 语言的运行时直接处理的,这使得它们的创建和销毁非常快速和高效。
    • Java 线程 的创建和管理则是由 JVM 和操作系统共同处理的,这通常比 goroutine 更复杂,因此也更耗费资源。
  4. 上下文切换:

    • Goroutine 之间的上下文切换通常比 Java 线程之间的上下文切换要快,因为它们需要保存和恢复的状态更少。

1、创建goroutine:

go func() {

    // 业务逻辑

}()

2、goroutine中发生了panic怎么办:

(1)使用recover函数来恢复程序的执行。

(2)recover函数只有在defer函数中调用才能起作用,它会捕获当前goroutine中的panic,并返回panic所传递的值。
  注意:recover函数只能在同一个goroutine中恢复panic,不能跨越多个goroutine。

go func() {

    defer func() {

        if err := recover(); err != nil {

            fmt.println(err)

        }

    }()

}()

3、协程间通信如何实现:

(1)channel 是一种用于协程间通信和同步的重要机制。

         当我们创建一个 channel 时,实际上是在内存中分配了一个 channel 对象,包括一个队列和一些控制信息,用于协程之间的数据传输。

(2)channel 对象是什么时候被销毁的?

         当没有任何协程在使用一个 channel 时,该 channel 会被垃圾回收器回收。
        也就是说,当所有使用该 channel 的协程都已经结束或退出时,该 channel 对象才会被销毁。

4、goroutine中使用channel,channel阻塞了怎么办:

       往已满的channel中发送数据 或 从一个空channel中接收数据时,channel 会阻塞当前 goroutine,等待操作的另一方执行相应的操作后才会继续执行。

       如果 channel 阻塞了,可以考虑以下几种方式:

    (1)、使用非阻塞式的 channel 操作:可以使用 select 语句进行非阻塞的 channel 操作。通过在 select 语句中使用 default 分支或者带有超时的 case 分支,可以在 channel 阻塞时执行其他操作或者超时退出。

    (2)、使用带缓冲的 channel:带缓冲的 channel 可以在一定程度上避免 channel 阻塞的问题。通过给 channel 设置缓冲区,可以在 channel 不满或不空时发送或接收数据,并避免阻塞。

    (3)、调整 goroutine 的数量:减少 goroutine 的数量或者增加并发操作的数量,可以在一定程度上避免 channel 阻塞的问题。

     扩展一:带超时的case分支:

// 在下面的代码中,使用 <-time.After(time.Second) 创建一个定时器,它将在 1 秒后向一个无缓冲的 channel 发送一个时间值。

// 因此,在 select 语句中,如果 1 秒内没有任何其他 channel 发生变化,就会执行超时操作。

// 需要注意的是,time.After 函数返回的是一个 channel,因此在 select 语句中需要使用 <- 符号来接收其返回值

select {

case <-ch1:

    // 处理 ch1 的数据

case <-ch2:

    // 处理 ch2 的数据

    // <-time.After(time.Second) 创建一个定时器,它将在 1 秒后向一个无缓冲的 channel 发送一个时间值。

case <-time.After(time.Second):

    // 在 1 秒后执行超时操作

}

5、goroutine是什么时候销毁的:

(1)启动一个 goroutine 时,实际上是在创建一个新的协程并在其中执行指定的函数,与主协程并发地执行。
         当函数执行结束后,该 goroutine 也就自然而然地被销毁了。
(2)需要注意的是,goroutine 的生命周期与其所在的程序进程相关联,它们在执行结束后会自动退出并被 Go 运行时系统回收。
         也就是说,只要程序进程没有结束,goroutine 就会一直存在,除非它自己调用了 return、panic 或 recover 函数,或者被调度器强制终止。
         在一般情况下,goroutine 在执行结束后就会自动退出,并被 Go 运行时系统回收。

6、goroutine 和 消息中间件 使用场景有什么区别:

(1)它俩都是在编写并发程序时常用的工具,但是使用场景有所不同。
(2)Goroutine 适用于处理大量短时间的任务,比如网络请求、IO 操作等。
(3)消息中间件是一种用于分布式系统中消息传递的工具,可以在不同的进程、主机甚至不同的系统之间传递消息,适用于大规模分布式系统中的任务调度、数据传输等场景。

因此,Goroutine 适用于单机并发场景,而消息中间件适用于分布式系统中的任务调度和消息传递场景。
当需要处理的任务量很大时,可以考虑使用消息中间件来进行任务调度和消息传递,而不是仅仅使用 Goroutine 来实现并发。

7、goroutine中调用远程服务,失败了怎么办:

(1)返回错误信息并重试:可以使用循环或递归等方式进行重试,直到成功或达到最大重试次数为止。注意:在进行重试时需要考虑到错误的类型和重试的间隔时间等因素,以免重复出现相同的错误。
(2)通过日志记录错误:在发生错误时,可以将错误信息记录到日志中,方便后续排查问题。
(3)抛出异常并结束程序:如果错误是无法处理的,比如服务端已经停止了服务,可以抛出异常并结束程序。在捕获异常时,可以使用recover 函数来捕获异常并进行处理。

  无论采用哪种方式来处理错误,在进行远程服务调用时,一定要做好异常处理和错误处理,以确保程序的稳定性和健壮性。

8、go语言recover知识点:


(1)仅在 defer 函数中有效:recover 函数只能在 defer 函数中使用。
(2)捕获 panic:recover 函数只能捕获到当前 goroutine 中的 panic。当 panic 被捕获后,程序会从 panic 处开始恢复执行。
(3)返回值:当 recover 函数捕获到一个 panic 时,它会返回传递给 panic 函数的值。如果 recover 函数没有捕获到任何 panic,则返回 nil。
(4)阻止程序崩溃:recover 函数的主要目的是阻止程序崩溃。当程序发生 panic 时,通过使用 recover 函数可以避免程序终止。

package main

import "fmt"

func main() {

    defer func() {

        if r := recover(); r != nil {

            fmt.Println("Recovered from:", r)

        }

    }()

    fmt.Println("Starting the program")

    panic("A panic occurred")

    fmt.Println("End of the program"// 这行代码将不会被执行,因为程序在 panic 处恢复执行。

}

// 输出

Starting the program

Recovered from: A panic occurred

请注意,尽管可以使用 recover 来处理异常,但在 Go 中,建议尽可能使用错误值而不是 panic 和 recover,因为错误值通常更容易理解和处理。
panic 和 recover 主要用于处理那些无法预料或难以处理的错误情况。

9、多个goroutine直接怎么保障顺序执行:


可以使用无缓冲的信道来实现同步,可以确保一个goroutine在另一个goroutine完成后才开始执行。

package main

import (

    "fmt"

    "time"

)

func firstTask(done chan bool) {

    fmt.Println("Executing first task...")

    time.Sleep(1 * time.Second)

    // 第一个goroutine完成时,向done1发送一个值

    done <- true

}

func secondTask(done chan bool) {

    fmt.Println("Executing second task...")

    time.Sleep(1 * time.Second)

    // 第二个goroutine在完成时向done2发送一个值

    done <- true

}

func main() {

    // 创建两个无缓冲的信道。

    done1 := make(chan bool)

    done2 := make(chan bool)

    // 当第一个goroutine完成时,它将向done1发送一个值,

    go firstTask(done1)

    // 主goroutine会阻塞等待接收这个值,当接收到这个值后,主goroutine才会继续执行第二个goroutine。

    <-done1

    // 第二个goroutine在完成时向done2发送一个值,

    go secondTask(done2)

    // 主goroutine阻塞等待接收这个值。

    <-done2

    // 当接收到这个值后,主goroutine将继续执行,输出 "All tasks completed."。

    fmt.Println("All tasks completed.")

}

在这个示例中,done1 和 done2 是无缓冲的信道。

当第一个goroutine完成时,它将向done1发送一个值,主goroutine会阻塞等待接收这个值,当接收到这个值后,主goroutine才会继续执行第二个goroutine。

类似地,第二个goroutine在完成时向done2发送一个值,主goroutine阻塞等待接收这个值。当接收到这个值后,主goroutine将继续执行,输出 "All tasks completed."。

10、开启一个goroutine,怎么控制关闭它:

可以使用一个 chan bool 类型的 channel 来控制一个 goroutine 的生命周期,从而实现控制其关闭的功能。
具体实现方式如下:
(1)在主函数或其他需要控制 goroutine 的地方,创建一个 bool 类型的 channel,并将其传递给要启动的 goroutine。
(2)在 goroutine 中,使用一个无限循环来监听这个 channel 的状态,如果 channel 接收到了信号,就退出循环,从而结束 goroutine。
(3)当需要关闭 goroutine 时,向该 channel 发送一个信号,通知 goroutine 退出循环。

func startWorker(stopCh chan bool) {

    for {

        // 在这里执行 goroutine 的任务逻辑

        // 在 goroutine 中,使用一个无限循环来监听这个 channel 的状态,如果 channel 接收到了信号,就退出循环,从而结束 goroutine。

        select {

        case <-stopCh:

            // 收到关闭信号,退出循环

            return

        default:

            // 如果没有收到关闭信号,则继续循环执行任务逻辑

        }

    }

}

func main() {

    // 主函数创建一个 bool 类型的 channel,并将其传递给要启动的 goroutine。

    stopCh := make(chan bool)

    go startWorker(stopCh)

    // 等待一段时间后关闭 goroutine

    time.Sleep(time.Second * 5)

    stopCh <- true

}

在上面的代码中,我们创建了一个 stopCh channel,然后在 startWorker 函数中通过一个无限循环来执行 goroutine 的任务逻辑。

在每次循环开始时,使用 select 语句来监听 stopCh channel 的状态。如果收到了关闭信号,就退出循环并结束 goroutine。
在 main 函数中,我们通过 go startWorker(stopCh) 启动了一个 goroutine。然后在等待 5 秒后,向 stopCh channel 发送了一个信号,通知 goroutine 结束循环并退出。

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

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

相关文章

Go 语言变量

变量来源于数学&#xff0c;是计算机语言中能储存计算结果或能表示值抽象概念。 变量可以通过变量名访问。 Go 语言变量名由字母、数字、下划线组成&#xff0c;其中首个字符不能为数字。 声明变量的一般形式是使用 var 关键字&#xff1a; var identifier type 可以一次声…

线程基础知识

进程是资源分配的最小单位&#xff0c;线程是程序执行的最小单位… 为什么使用线程 多线程之间会共享同一块地址空间和所有可用数据的能力&#xff0c;这是进程所不具备的线程要比进程更轻量级 &#xff0c;由于线程更轻&#xff0c;所以它比进程(fork创建进程以执行新的任务…

Postgresql 从小白到高手 十一 :数据迁移ETL方案

文章目录 Postgresql 数据迁移ETL方案1、Pg 同类型数据库2 、Pg 和 不同数据库 Postgresql 数据迁移ETL方案 1、Pg 同类型数据库 备份 : pg_dump -U username -d dbname -f backup.sql插入数据&#xff1a; psql -U username -d dbname -f backup.sqlpg_restore -U username…

远程桌面连接服务器怎样连接不上的六个常见原因

远程桌面连接服务器无法连接的问题可能由多种原因引起。以下是一些常见的问题及其解决方案&#xff1a; 1. 网络连接问题&#xff1a;远程桌面连接的基础是稳定的网络连接。如果网络连接不稳定或中断&#xff0c;那么你将无法连接到远程桌面。检查你的网络连接&#xff0c;确保…

Codigger数据篇(中):数据可控性的灵活配置

在数据服务领域中&#xff0c;数据可控性无疑是至关重要的一环。数据可控性不仅关乎数据的安全性和隐私性&#xff0c;更直接影响到数据价值的实现。Codigger&#xff0c;在其数据可控性方面的灵活配置&#xff0c;为用户提供了更加便捷、高效的数据管理体验。 一、自主选择数…

Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步

1. Spring6 当中 Bean 的生命周期的详细解析&#xff1a;有五步&#xff0c;有七步&#xff0c;有十步 文章目录 1. Spring6 当中 Bean 的生命周期的详细解析&#xff1a;有五步&#xff0c;有七步&#xff0c;有十步每博一文案1.1 什么是 Bean 的生命周期1.2 Bean 的生命周期 …

ThinkPHP Lang多语言本地文件包含漏洞(QVD-2022-46174)漏洞复现

1 漏洞描述 ThinkPHP是一个在中国使用较多的PHP框架。在其6.0.13版本及以前&#xff0c;存在一处本地文件包含漏洞。当ThinkPHP开启了多语言功能时&#xff0c;攻击者可以通过lang参数和目录穿越实现文件包含&#xff0c;当存在其他扩展模块如 pear 扩展时&#xff0c;攻击者可…

esp32学习

开启自动补全功能 Arduino IDE 2.0开启代码补全及修改中文_arduino ide怎么设置中文-CSDN博客 PWM 、 ADC转换 在使用这个adc默认配置的时候adc引脚的输入电压必须是介于0-1之间&#xff0c;如何高于1v的电压都会视为一个最高值&#xff0c;如果要增加测量电压你就需要配置一…

【JAVA】part5-Java集合

Java 集合 Java集合概述 Java数组的局限性 数组初始化后大小不可变&#xff1b;数组只能按索引顺序存取。 Java的java.util包主要提供了以下三种类型的集合&#xff1a; List&#xff1a;一种有序列表的集合&#xff0c;例如&#xff0c;按索引排列的Student的List&#xff1b…

车载气象站:可移动监测的气象站

TH-CZ5车载气象站是一种专门针对车辆、船舶等应急环境检测设备而设计的可移动监测的气象站。 一、系统介绍 车载气象站系统采用先进的高精度GPS及三轴电子罗盘&#xff0c;可实现车行驶时的风速、风向检测。整机为野外型设计&#xff0c;同时还可对气温、相对湿度、雨量、气压…

Linux修改文件权限命令 chmod

【例子引入】 以下面命令为例&#xff1a; chmod 777 Random.py 当写入下面名为Random.py的代码后&#xff1a; 如果直接运行&#xff0c;会显示权限不够 当输入 chmod 777 Random.py 更改权限后&#xff0c;才能够正常运行 在终端中输入 这条命令是关于Linux或Unix-like系…

FlaUI

FlaUI是一个基于微软UIAutomation技术&#xff08;简称UIA&#xff09;的.NET库&#xff0c;它主要用于对Windows应用程序&#xff08;如Win32、WinForms、WPF、Store Apps等&#xff09;进行自动化UI测试。FlaUI的前身是TestStack.White&#xff0c;由Roemer开发&#xff0c;旨…

Socket编程--TCP连接以及并发处理

流程图 网络传输流程&#xff1a; TCP连接&#xff1a; api 客户端&#xff1a; socket: 创建套接字 domain: AF_INET &#xff1a;IPv4 type: SOCK_STREAM(tcp)、SOCK_DGRAM&#xff08;udp&#xff09; protocol: 0 默认协议 返回值&#xff1a;成功返回一个新的套接字…

Linux-进程间通信(进程间通信介绍、匿名管道原理及代码使用、命名管道原理及代码使用)

一、进程通信介绍 1.1进程间通信的目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享同样的资源。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某…

值得买科技新思路,导购电商的终点是“AI+出海”?

在以往&#xff0c;大众普遍认为品牌的消费者大多是高度忠诚人群&#xff0c;而事实上&#xff0c;非品牌忠诚者相比重度消费者&#xff0c;对促进品牌增长更为重要。 这类非品牌忠诚者被定义为摇摆的消费者群体&#xff0c;也就是那些购买品牌产品概率在20%-80%之间的消费者。…

【Unity动画系统】Animator组件的属性

介绍Animator组件的全部属性 Controller&#xff1a;动画控制器 Avatar&#xff1a;人物骨骼 Apply Root Motion&#xff1a;有一些动画片段自带位移&#xff0c;如果希望自带的位移应用在游戏对象上&#xff0c;那么就勾选&#xff1b;如果自己编写脚本&#xff0c;那么就不…

如何用智能获客开启新商机?揭秘赢销侠软件的奇效

在当今数字化竞争日益激烈的商业环境中&#xff0c;企业为了生存和发展&#xff0c;必须寻找新的途径以获取潜在客户。智能获客作为一种新型的营销方式&#xff0c;正以其高效、精准的特点改变着传统的市场开拓模式。而在这个过程中&#xff0c;自动获客软件的作用愈发凸显&…

LLM大语言模型原理、发展历程、训练方法、应用场景和未来趋势

LLM&#xff0c;全称Large Language Model&#xff0c;即大型语言模型。LLM是一种强大的人工智能算法&#xff0c;它通过训练大量文本数据&#xff0c;学习语言的语法、语义和上下文信息&#xff0c;从而能够对自然语言文本进行建模。这种模型在自然语言处理&#xff08;NLP&am…

杰发科技AC7840——SPI通信简介(1)_跑通Demo

0. 简介 一些配置项&#xff1a; CPHA&#xff1a;相序 CPLO&#xff1a;极性 看着demo需要按键&#xff0c;于是去掉按键&#xff0c;去掉打印&#xff0c;直接输出波形看逻辑分析仪的信号。 其实现在做这些demo测试应该都有逻辑分析仪&#xff0c;直接看波形更直观一点。…

分享:抖音老阳口中的选品师项目好做吗?

近年来&#xff0c;随着抖音等短视频平台的兴起&#xff0c;越来越多的博主通过分享自己的生活、知识和见解吸引了大量粉丝。其中&#xff0c;抖音博主老阳以其独特的选品眼光和专业的产品评测&#xff0c;在广大网友中树立了良好的口碑。那么&#xff0c;老阳口中的选品师项目…
最新文章