Golang笔记(下)

Golang学习笔记(下)

前篇:Golang学习笔记(上)

在这里插入图片描述

十四、错误处理

14.1使用error类型
func New(text string) error

例子:

package main

import (
    "errors" // 导入errors包
    "fmt"
)

func main() {
    var number, divisor int
    fmt.Println("请输入一个整数作为分子:")
    fmt.Scan(&number)
    fmt.Println("请输入一个整数作分母:")
    fmt.Scan(&divisor)
    result, err := divide(number, divisor)
    fmt.Println("result", result)
    if err != nil {
        fmt.Println(err)
    }
}

// 除法函数,参数number是分子,参数divisor是分母
func divide(number, divisor int) (float32, error) {
    if divisor == 0 {
        // 返回多值数据
        return 0.0, errors.New("分母不能为0")
    }
    return float32(number / divisor), nil
}

14.2错误信息格式化

使用fmt包中的Errorf函数

func divide(number, divisor int) (float32, error) {
    if divisor == 0 {
        // 返回多值数据
        error := fmt.Errorf("您输入的分母是:%d", divisor)
        return 0.0, error
    }
    return float32(number / divisor), nil
}

14.3自定义错误类型
// 自定义错误类型
type error interface {
    Error() string
}

举例

package main

import (
    "fmt"
)

// 自定义错误类型结构体
type DivisionByZero struct {
    // 错误信息
    message string
}

// 实现error接口的Error()方法
func (d DivisionByZero) Error() string {
    return d.message
}

// 除法函数,参数number是分子,参数divisor是分母
func divide(number, divisor int) (float32, error) {
    if divisor == 0 {
        // 创建一个DivisionByZero错误实例,并返回
        return 0.0, DivisionByZero{message: "除数不能为0"}
    }
    return float32(number) / float32(divisor), nil
}

func main() {
    var number, divisor int
    fmt.Println("请输入一个整数作为分子:")
    fmt.Scan(&number)
    fmt.Println("请输入一个整数作分母:")
    fmt.Scan(&divisor)
    result, err := divide(number, divisor)
    if err != nil {
        // 打印具体的错误信息
        fmt.Println("发生错误:", err.Error())
        return
    }
    fmt.Printf("result: %f\n", result)
}

14.4错误机制
  • go语言提供了panic()和recover()函数

  • defer关键字实现错误处理

延迟执行

defer语句用来在函数最终要返回前被执行,用来释放资源,在错误处理的过程中,一个非常重要的环节就资源释放,无论执行成功,还是执行错误,都应该保证释放这些资源,此时可以使用defer关键字延迟执行,保证在程序运行完成之前释放资源。

当有多个defer语句时,defer语句遵守后进先出原则。

进入宕机状态

用于触发一个运行时错误。

  1. 自动进入当即状态,当发生运行期错误时自动进入。

  2. 手动触发,程序员可以根据自己的需要通过panic()函数可以手动触发宕机状态。

package main

import "fmt"

func main() {
    intSclical := []int{1, 2, 3}
    // 下标月结引发的宕机
    fmt.Println(intSclical[3])
}

输出:

D:Go/bin/go.exe run panic.go [F:/Code/Go/src]
panic: runtime error: index out of range [3] with length 3

goroutine 1 [running]:
main.main()
    F:/Code/Go/src/panic.go:8 +0x15
exit status 2
错误: 进程退出代码 1.

手动宕机

package main

import "fmt"

func main() {
    intSclical := []int{1, 2, 3}
    // 下标超出索引范围,自动引发的宕机
    // fmt.Println(intSclical[3])
    var index int = -1
    fmt.Println("请输入下标索引: ")
    fmt.Scan(&index)
    if index > 3 {
        panic("索引超出范围")
    } else {
        fmt.Println("输出元素为:", intSclical[index])
    }
}

14.5从宕机状态恢复

使用recover()函数从宕机状态恢复

package main

import (
    "fmt"
)

// 除法函数,参数number是分子,参数divisor是分母
func divide(number, divisor int) (float32, bool) {
    if divisor == 0 {
        // 触发panic,表示发生了严重的错误
        panic("除数不能为0")
    }
    return float32(number) / float32(divisor), true
}

func main() {
    var number, divisor int
    fmt.Println("请输入一个整数作为分子:")
    fmt.Scan(&number)
    fmt.Println("请输入一个整数作分母:")
    fmt.Scan(&divisor)

    // 使用defer和recover来捕获panic
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("程序遇到严重错误:", r)
        }
    }()

    result, ok := divide(number, divisor)
    if !ok {
        // 实际上,由于我们使用了panic,这里的ok检查是多余的
        // 因为panic会导致程序停止执行,不会到达这里
        fmt.Println("发生错误,无法执行除法")
        return
    }
    fmt.Printf("result: %f\n", result)
}

十五、并发编程

15.1进程
  • 一个进程就是一个执行中的程序

  • 每一个进程都有自己独立的一块内存空间

  • 每一个进程都有一组系统资源

15.2线程
  • 线程和进程都是一段完成特定功能的代码。

  • 它们都是程序中的单个顺序控制流程

  • 多个线程共享一块内存空间和一组系统资源。

  • 在切换不同线程之间时,系统的开销比切换进程要小得多,称为轻量级进程。

  • 一个进程可以包含多个线程。

15.3协程
  • 协程(Goroutines)是一种轻量级的线程。

  • 不阻塞线程,但可以挂起的计算过程。

  • 挂起几乎没有开销。

  • 底层库处理一部阻塞任务。

  • 协程代码流程顺序,无需大量的回调函数。

  • 类似同步代码,已于理解、调试和开发。

15.4创建协程
  • go语言的并发编程时通过协程(Goroutines)实现的。

  • 在Go中创建协程很简单,只要在函数或方法前加go关键字,则会创建一个新协程。

创建协程示例

package main

import "fmt"

func display(str string) {
    fmt.Println(str)
}

func main() {
    // 创建协程,异步调用函数
    go display("welcome to Beijing!")
    // 正常调用函数
    display("Hello World")
}

每次运行的结果不一致,原因

在这里插入图片描述

也就是说主线程会调用display(“Hello World!”)和协程中的dispaly(“Welcome to Beijing!”),这两调用的过程时随机的,谁先谁后不一定。

两个协程执行是异步的,哪个先执行,哪个后执行,都是不确定的,中就是并发程序的特点。

15.5通道

在这里插入图片描述

声明通道

  1. 使用chan关键字声明,语法如下:
    var Channel_name chan Type

  2. 使用make()函数创建通道,语法如下:
    channel_name := make(chan Type)

举例


package main

import "fmt"

func main() {
	// 1.使用chan声明通道
	var mychannel1 chan int
	fmt.Printf("mychannel1:%V\n", mychannel1)
	fmt.Printf("mychannel1类型:%T\n", mychannel1)

	// 2.通过make()函数创建通道
	mychannel2 := make(chan int)
	fmt.Printf("mychannel2:%v\n", mychannel2)
	fmt.Printf("mychannel类型:%T\n", mychannel2)
}

发送和接收数据

  1. 向通道发生数据语法如下:

    mychannel<-data

  2. 从通道接收数据语法如下:

    接收数据变量:=<-mychannel

关闭通道

使用close()函数关闭通道。

示例代码:

package main

import "fmt"

func display(chstr chan string) {
	// 向通道发生数据
	chstr <- "Hello,World!"
}

func main() {
	// 1.使用chan声明通道
	var mychannel1 chan int
	fmt.Printf("mychannel1:%V\n", mychannel1)
	fmt.Printf("mychannel1类型:%T\n", mychannel1)

	// 2.通过make()函数创建通道
	mychannel2 := make(chan int)
	fmt.Printf("mychannel2:%v\n", mychannel2)
	fmt.Printf("mychannel类型:%T\n", mychannel2)

	// 发生和接收数据
	message := make(chan string)
	// 启动协程
	go display(message)
	// 从通道中接收数据
	msg := <-message
	fmt.Println(msg)

	// 关闭通道
	ch := make(chan int, 3)
	ch <- 2
	ch <- 3
	close(ch)
	fmt.Println("关闭通道")

	// 试图向关闭的通道发生数据(error)
	// ch <- 4
	fmt.Println("关闭通道")

	// 试图从关闭的通道中接收数据
	msg1 := <-ch
	fmt.Println("从通道中接收数据")
	mgs2, ok := <-ch
	fmt.Println("从通道中接收的数据:", msg)
	fmt.Println(ok)
	fmt.Println(msg1, mgs2)
}

遍历通道

package main

import "fmt"

// 遍历通道
func producer(chnl chan int) {
	for i := 0; i < 10; i++ {
		chnl <- i
	}
}

func main() {
	// 创建int类型通道
	ch := make(chan int)
	// 创建协程
	go producer(ch)
	// 遍历从通道中接收的数据
	for v := range ch {
		fmt.Println("接受:", v)
	}
}

单向通道和双向通道

channel_name := make(<-chan Typr) // 只接收数据

channel_name := make(chan<- Typr) // 只发送数据

package main

import "fmt"

func main() {
	// 创建只接收的数据通道
	mychannel1 := make(<-chan string)

	// 创建只发送的数据通道
	mychannel2 := make(chan<- string)
	fmt.Printf("mychannel1:%v\n", mychannel1)
	fmt.Printf("mychannel1类型:%T\n", mychannel1)
	fmt.Printf(<-mychannel2) // error:invalid operation: cannot receive from send-only channel mychannel2 (variable of type chan<- string)

	fmt.Printf("mychannel2:%v\n", mychannel2)
	fmt.Printf("mychannel2类型:%T\n", mychannel2)
}	

缓冲区通道

  • 无缓冲区通道

无缓冲区通道,通道中无法存储数据。发送和接收数据时是同步的,无缓冲区通道使用make()函数创建,自谦创建的都是无缓冲区通道。

有两种情况下会造成程序的阻塞:

  1. 当一个协程A将数据发送给通道时,其他协程还未接收数据时,协程A被迫阻塞,知道其他协程接收数据后,协程A才能继续执行。

  2. 当一个协程A试图从通道接收数据时,如果通道A中没有数据,则会等待其他协程发送,知道其他协程发送数据后,协程A才能继续执行。

  • 有缓冲区通道

有缓冲区通道,通道中可以存储数据,发送和接受时是异步的,创建有缓冲区的通道可以使用make(chan Type int)函数,

该函数的第一个参数是通道中的数据类型,第二个参数是设置缓冲区的大小。

在这里插入图片描述

15.6使用select语句

语法:

select {
    case 通道1:
        语句组1
    case 通道2:
        语句组2
    ...
    case 通道n:
        语句组n
    default:
        语句组n+1
}

举例:

package main

import (
	"fmt"
	"time"
)

func process(ch chan string) {
	time.Sleep(5 * time.Second)
	ch <- "Hello World."
}

func main() {
	// 声明两个通道c1和c2
	c1 := make(chan string)
	c2 := make(chan string)

	// 协程处理匿名函数
	go func() {
		// 休眠1秒
		time.Sleep(1 * time.Second)
		c1 <- "one"
	}()
	// 协程处理匿名函数
	go func() {
		// 休眠2秒
		time.Sleep(2 * time.Second)
		// 发送数据
		c2 <- "two"
	}()

	// 循环遍历从通道中接收的数据
	for i := 0; i < 2; i++ {
		select {
		// 从c1通道接收数据
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		// 从c2通道接收数据
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}

	// default
	ch := make(chan string)
	// 启动协程
	go process(ch)
	// 一直遍历从通道中取数据
	for {
		time.Sleep(1 * time.Second)
		select {
		case v := <-ch:
			fmt.Println("接收数据:", v)
			return
		default:
			fmt.Println("没有数据接收。")
		}
	}
}
	for {
		time.Sleep(1 * time.Second)
		select {
		case v := <-ch:
			fmt.Println("接收数据:", v)
			return
		default:
			fmt.Println("没有数据接收。")
		}
	}
}

十六、正则表达式

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

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

相关文章

【数据结构】树与二叉树遍历算法的应用(求叶子节点个数、求树高、复制二叉树、创建二叉树、二叉树存放表达式、交换二叉树每个结点的左右孩子)

目录 求叶子节点个数、求树高、复制二叉树、创建二叉树、二叉树存放表达式、交换二叉树每个结点的左右孩子应用一&#xff1a;统计二叉树中叶子结点个数的算法写法一&#xff1a;使用静态变量写法二&#xff1a;传入 count 作为参数写法三&#xff1a;不使用额外变量 应用二&am…

【Linux】socket编程2

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;客户端代码Makefile(生成目标文件)UdpClient.cc(客户端代码)服务端代码部分优化1&#xff08;接受客户端时显示客…

基于51单片机低中高音7键电子琴音乐播放器

基于51单片机电子琴音乐播放器 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.可以使用按键切换音乐播放模式和弹奏模式&#xff1b; 2.LED灯显示在使用哪种模式&#xff1b; 3.音乐…

Redis部署之主从

使用两台云服务器&#xff0c;在 Docker 下部署。 Redis版本为&#xff1a;7.2.4 下载并配置redis 配置文件 下载 wget -c http://download.redis.io/redis-stable/redis.conf配置 master节点配置 bind 0.0.0.0 # 使得Redis服务器可以跨网络访问,生产环境请考虑…

第十四届蓝桥杯C/C++大学B组题解(二)

6、岛屿个数 #include <bits/stdc.h> using namespace std; const int M51; int T,m,n; int vis[M][M],used[M][M]; int dx[]{1,-1,0,0,1,1,-1,-1}; int dy[]{0,0,1,-1,1,-1,1,-1}; string mp[M]; struct node{//记录一点坐标 int x,y; }; void bfs_col(int x,int y){ qu…

C++ | Leetcode C++题解之第14题最长公共前缀

题目&#xff1a; 题解&#xff1a; class Solution { public:string longestCommonPrefix(vector<string>& strs) {if (!strs.size()) {return "";}int minLength min_element(strs.begin(), strs.end(), [](const string& s, const string& t)…

同步压缩理论

参考 在频率方向进行能量重新分配&#xff08;分配到中心&#xff09; 时频重排

实验4 DHCP基础配置

实验4 DHCP基础配置 一、 原理描述二、 实验目的三、 实验内容四、 实验配置五、 实验步骤1.基本配置2.配置DHCPServer功能3.配置DHCP Client 一、 原理描述 动态主机配置协议 DHCP是一个局域网的网络协议&#xff0c;使用UDP协议工作&#xff0c;主要有两个用途&#xff1a;用…

大话设计模式——16.命令模式(Command Pattern)

简介 请求以命令的形式包裹在对象中&#xff0c;并传给调用对象。调用对象寻找可以处理该命令的对象进行执行。命令模式是一种特殊的策略模式&#xff0c;体现多个策略执行的问题&#xff0c;而不是选择的问题 UML图 应用场景 界面选择、键盘、按钮、事件操作都类似命令模式 …

前端工程化理解 (2024 面试题)

最好介绍远古世界最好随性一点&#xff0c;不要太刻板 &#xff0c;不然像背书 什么是前端工程化&#xff1f; - 知乎 前端工程化的历史 互联网初期&#xff0c;09 年以前&#xff0c;页面只需要展示一些列表、表格、文章内容以及简单图片即可&#xff0c;其目的是为了传送信…

身份证正面打印、反面打印与打印在同一页

1 打印身份证正面 将身份证正面朝下放置&#xff0c;按打印机键盘上的数字【3】&#xff0c;再按【Start】键&#xff0c;选择A4大小打印&#xff0c;如图(1)所示&#xff1a; 图(1) A4纸复印 2 打印身份证反面 将身份证反面朝下放置&#xff0c;按打印机键盘上的数字【3】&…

KVM 高级功能部署

目录 一、案例分析 1.1、案例概述 1.2、案例前置知识点 1&#xff09;KVM 虚拟机迁移 2&#xff09;KSM 内核同页合并 1.3、案例环境 1&#xff09;本案例环境 2&#xff09;案例需求 3&#xff09;案例实现思路 二、案例实施 2.1、静态迁移 1&#xff09;在…

springboot+vue药店药品进销存采购管理系统0z10z

本系统采用intellij idea支持eclipse 项目架构&#xff1a;B/S架构web 开发语言&#xff1a;java 前端技术&#xff1a;vue.jsElementUi 后端框架&#xff1a;django、mybatis、Springmvc 运行环境&#xff1a;win10/win11、jdk1.8 可行性论证 社会可行性 开发本系统&#xff…

XC6206稳压芯片

mark 662k XC6206 的基本特性。这是一个 SOT23封装的 3.3V 稳压器。它输出最大工作电流为 100mA 最大特点便宜 参考链接 XC6206稳压芯片 (qq.com)https://mp.weixin.qq.com/s?__bizMzA5NjQyNjc2NQ&mid2452268489&idx1&sn90e920c596e3c2a382f81929c6313977&c…

Linux--进程的概念(一)

目录 一、冯诺依曼体系结构二、操作系统2.1 什么是操作系统2.2 操作系统的意义 三、进程3.1 进程的基本概念3.2 描述进程——PCB3.3 进程和程序的区别3.4 task_struct-PCB的一种3.5 task_struct的内容分类 四、如何查看进程4.1 通过系统文件查看进程4.2 通过ps指令查看进程 五、…

《公安机关互联网安全监督检查规定》系列之“解决方案”

随着中国互联网和信息网络飞速发展&#xff0c;无线网络普及到国内各个家庭和公共场所&#xff0c;成为人们日常办公和生活娱乐不可或缺的一部分。无线网络在创造商业价值、带来工作和生活便捷的同时&#xff0c;也同样让犯罪份子有了可乘之机&#xff0c;越来越多的网络违法活…

如何通过数据验证防止 Web API 攻击 - Web API 安全指南

充分的数据保护和用户保密是网页开发者的主要责任。因此&#xff0c;在构建 API 终端时&#xff0c;确保最高可能的安全性至关重要。 应用程序安全是客户端和服务器开发者共同的责任&#xff0c;一方的疏忽可能会造成灾难性后果。统计数据显示&#xff0c;2023 年的数据泄露导…

windows安装Redis,Mongo,ES并快速基本掌握开发流程

前言 这里只是一些安装后的基础操作&#xff0c;后期会学习更加深入的操作 基础操作 前言RedisRedis启动idea集成Redisjedis技术 Mongodbwindows版Mongodb的安装idea整合Mongodb ES(Elasticsearch)ESwindows下载ES文档操作idea整合ES低级别ES整合高级别ES整合 Redis Redis是…

HarmonyOS 开发-Grid和List内拖拽交换子组件位置

介绍 本示例分别通过onItemDrop()和onDrop()回调&#xff0c;实现子组件在Grid和List中的子组件位置交换。 效果图预览 使用说明&#xff1a; 拖拽Grid中子组件&#xff0c;到目标Grid子组件位置&#xff0c;进行两者位置互换。拖拽List中子组件&#xff0c;到目标List子组件…

【进阶六】Python实现SDVRPTW常见求解算法——自适应大邻域算法(ALNS)

基于python语言&#xff0c;采用经典自适应大邻域算法&#xff08;ALNS&#xff09;对 带硬时间窗的需求拆分车辆路径规划问题&#xff08;SDVRPTW&#xff09; 进行求解。 目录 往期优质资源1. 适用场景2. 代码调整2.1 需求拆分2.2 需求拆分后的服务时长取值问题 3. 求解结果4…