Go语言学习16-特殊流程控制

2023-10-27

引言

上一篇博文介绍了 Go 语言的基本流程控制,本篇我们介绍 Go 语言的特殊流程控制

特殊流程控制

1. defer语句

defer 语句被用于预定对一个函数的调用。被 defer 语句调用的函数称为延迟函数。defer 语句只能出现在函数或方法的内部。

一条 defer 语句总是以关键字 defer 开始。在 defer 的右边还会有一条表达式语句,且它们之间要以空格分隔。例如:

defer fmt.Println("The finishing touches.")

如上的表达式语句必须代表一个函数或方法的调用。但是像针对各种内建函数的那些调用表达式,因为它们并不能称为表达式语句,所以不允许出现在这里。同时这个位置出现的表达式语句是不能被圆括号括起来的。

defer 语句的执行时机总是在直接包含它的那个函数(简称外围函数)把流程控制权交还给它的调用方的前一刻,无论 defer 语句出现在外围函数的函数体中的哪一个位置上。具体分为:

  • 当外围函数的函数体中的相应语句全部被正常执行完毕的时候,只有在该函数中的所有 defer 语句都被执行完毕之后该函数才会真正地结束执行。

  • 当外围函数的函数体中的 return 语句被执行的时候,只有在该函数中的所有 defer 语句都被执行完毕之后该函数才会真正地返回。

  • 当在外围函数中有运行时恐慌发生的时候,只有在该函数中的所有 defer 语句都被执行完毕之后该运行时恐慌才会真正地被扩散至该函数的调用方。

也就是说,外围函数的执行的结束会由于其中的 defer 语句的执行而被推迟。例如:

func isPositiveEnenNumber(number int) (result bool){
	defer fmt.Println("done.");
	if number < 0 {
		panic(errors.New("The number is a negative number!"))
	}
	if number % 2 ==0 {
		return true
	}
	return
}

上述示例中,无论参数 number 是怎样的值,以及该函数的执行会以怎样的方式结束,在该函数的调用方重获流程控制权之前标准输出上都一定会打印 done.

综上总结,使用 defer 语句的优势有两个:

  • 收尾任务总会被执行,这样就不会因粗心大意而造成资源的浪费。

  • 可以把它们放到外围函数的函数体中的任何地方(一般是函数体开始处或紧跟在申请资源的语句的后面),而不是只能放在函数体的最后。

defer 语句中,调用的函数不但可以是已声明的命名函数,还可以是临时编写的匿名函数。例如:

defer func(){
	fmt.Println("The finishing touches.")
}()

注意: 一个针对匿名函数的调用表达式是由一个函数字面量和一个代表了调用操作的 一对圆括号 组成的。

无论在 defer 关键字右边的是命名函数还是匿名函数,都可以称为 延迟函数。因为它总是会被延迟到外围函数执行结束前一刻才被真正地调用。每当 defer 语句被执行的时候,传递给延迟函数的参数都会以通常的方式被求值。如下:

func begin(funcName string) string {
	fmt.Printf("Enter function %s.\n", funcName)
	return funcName
}

func end(funcName string) string {
	fmt.Printf("Exit function %s.\n", funcName)
	return funcName
}

func record(){
	defer end(begin("record"))
	fmt.Println("In function record")
}

对函数 record 进行调用之后,运行截图如下:

这里写图片描述

出于同一条 defer 语句可能会被多次执行的考虑,如下:

func printNumbers(){
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)
	}
}

对函数 printNumbers 进行调用之后,运行截图如下:

这里写图片描述

如上的函数 printNumbers 有两点需要关注:

  • 在for语句的每次迭代的过程中都会执行一次其中的defer语句。Go语言会把代入参数值之后的调用表达式另行存储,以此类推,后面几次迭代所产生的延迟函数调用表达式依次为:

    fmt.Printf("%d ", 0)
    fmt.Printf("%d ", 1)
    fmt.Printf("%d ", 2)
    fmt.Printf("%d ", 3)
    fmt.Printf("%d ", 4)
    
  • 对延迟函数调用表达式的求值顺序是与它们所在的defer语句被执行的顺序完全相反的。每当Go语言把已带入参数值的延迟函数调用表达式另行存储之后,还会把它追加到一个专门为当前外围函数存储延迟函数调用表达式的列表(也就是栈)当中,而该列表总是先进后出。因此这些延迟函数调用表达式的求值顺序会是:

    fmt.Printf("%d ", 4)
    fmt.Printf("%d ", 3)
    fmt.Printf("%d ", 2)
    fmt.Printf("%d ", 1)
    fmt.Printf("%d ", 0)
    

我们再看看下面的例子,如下:

func appendNumber(ints []int) (result []int) {
	result = append(ints, 1)
	defer func(){
		result = append(result, 2)
	}()
	result = append(result, 3)
	defer func(){
		result = append(result, 4)
	}()
	result = append(result, 5)
	defer func(){
		result = append(result, 6)
	}()
	return result
}
func main(){
	res := appendNumber([]int{0})
	fmt.Printf("result: %v\n", res)
}

运行结果截图如下【大家可以试着按上面的两点去分析下】:

这里写图片描述

现在考虑一个问题,把 printNumbers 函数的声明修改为如下:

func printNumbers(){
	for i := 0; i < 5; i++ {
		defer func() {
			fmt.Printf("%d ", i)
		}()
	}
}

运行结果截图如下:

这里写图片描述

现在我们对运行结果进行分析可知:

for 语句被执行完毕的时候,共有 5 个相同的延迟函数调用表达式被存储在专属列表()中,例如:

func() {
	fmt.Printf("%d ", i)
}()

这时的变量 i 已经被修改为了 5,对 5 个相同的调用表达式的求值都会使标准输出打印出 5

针对上面的情况,可以修改如下:

defer func(i int) {
	fmt.Printf("%d ", i)
}(i) // 在defer语句被执行的时候,传递给延迟函数的这个参数i就会被求值。

运行结果截图如下(这个和第一个版本的 printNumbers 函数执行效果是相同的):

这里写图片描述

如果 延迟函数 是一个匿名函数,并且在 外围函数 的声明中存在命名的结果声明,那么在延迟函数中的代码使可以对命名结果的值进行访问和修改的。例如:

func modify(n int) (number int) {
	defer func(){
		number += n
	}()
	number++
	return
}

延迟函数 的声明中可以包含结果声明,但是其返回的结果值会在它被执行完毕时被丢弃。因此在编写延迟函数的声明的时候不会为其添加结果声明。另外,推荐以传参的方式提供延迟函数所需的外部值。例如:

// 传入参数为1时,modify函数的结果值是5
func modify(n int) (number int) {
	defer func(plus int) (result int){
		result = n + plus
		number += result
		return // 此处虽然返回了结果,但是却并不会产生任何效果。
	}(3) // 延迟函数调用时直接传外部参数
	number++
	return
}

2. 异常处理

在前面的博文中已经涉及了Go语言的异常处理的知识,比如 接口类型error内建函数panic标准库代码包errors。本小节将对Go语言的各种异常处理方法进行系统的讲解。

2.1 error

在Go语言标准库代码包中的很多函数和方法会返回 error 类型值来表明错误状态及其详细信息。error 是一个预定义标识符,它代表了一个Go语言内建的接口类型。该接口类型声明如下:

type error interface {
	Error() string
}

其中,Error 方法声明的意义就在于为方法调用方提供当前错误状态的详细信息。任何数据类型只要实现了这个可以返回 string 类型值的 Error 方法就可以成为一个 error 接口类型的实现。但在通常情况下,不需要自己编写一个 error 的实现类型,Go语言的标准库代码包 errors 为我们提供了一个用于创建 error 类型值的函数 New,声明如下:

func New(text string) error {
	return &errorString(text) // 返回一个error类型值,它的动态类型就是errors.errorString类型
}

errors.errorString的名称上可知,errorString 的首字母小写,该类型是一个包级私有的类型。它只是errors 包的内部实现的一部分,而非公开的 APIerrors.errorString 类型及其方法的声明如下:

type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

打印error类型值所代表的错误的详细信息。

var err error = errors.New("A normal error example")
fmt.Println(err)
fmt.Printf("%s\n", err)

另一个可以生成 error 类型值的方法是调用 fmt 包中的 Errorf 函数,调用类似如下代码:

// 初始化一个error类型值并作为该函数的结果值返回给调用方。
err2 := fmt.Errorf("%s\n","A normal error")

fmt.Errorf 函数的内部,创建和初始化 error 类型值的操作正是通过调用 errors.New 函数来完成的。

结构体类型 os.PathError 是一个 error 接口类型的实现类型。声明及其方法如下:

// PathError records an error and the operation and file path that caused it.
type PathError struct {
	Op  string  // “open” , ”unlink”, etc
	Path string // The associated file
	Err  error  // Returned by the system call
}

func (e *PathError) Error() string { 
	return e.Op + " " + e.Path + ": " + e.Err.Error() 
}

先判定获取到的 error 类型值的动态类型,再依次来进行必要的类型转换和后续操作。例如:

file , err := os.Open("E:\\Software\\lgh.txt")
if err != nil {
	if pe, ok := err.(*os.PathError); ok {// 判断err是否为*os.PathError类型
		fmt.Printf("Path Error: %s \n(op=%s,path=%s)\n", pe.Err, pe.Op, pe.Path)	
	} else {
		fmt.Printf("Unknown Error: %s\n",err)
	}
}

如上Open 的参数的文件路径不存在,运行截图如下:

这里写图片描述

在上面示例中的 os.Open 函数在执行过程中没有发生任何错误,就可以对变量 file 的内容进行读取了。例如:

reader := bufio.NewReader(file) // 创建一个可以读取文件内容的读取器
var buf bytes.Buffer // 缓存从文件读取出来的内容
for {
	// reader读取器,返回3个结果值。reader类型所属的方法如下:
	// func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
	// 当读取器读到file所代表的文件的末尾时,ReadLine方法会直接将变量io.EOF的值作为它的第三个结果值err返回。
	byteArray, _, err1 := reader.ReadLine()
	if err1 != nil {
		// io.EOF是error类型的变量,在标准库代码包io中,它的声明如下:
		// var EOF = errors.New("EOF") ,EOF是文件结束符(End Of File)的缩写。
		// 严格来说,EOF并不应该算作一个真正的错误,而仅仅属于一种"错误信号"
		if err1 == io.EOF { // 判断读取器是否已经读到了文件的末尾
			break
		} else {
			fmt.Printf("Read Error: %s\n", err1)
			break
		}
	} else {
		buf.Write(byteArray)
	}
	fmt.Printf("%s\n", byteArray)
}

实现 error 接口类型的另一个技巧是,可以通过把 error 接口类型嵌入到新的接口类型中来对它进行扩展。标准库代码包 net 中的 Error 接口类型,声明如下:

//An Error represents a network error
type Error interface {
	error
	Timeout() bool // Is the error a timeout?
	Temporary() bool // Is the error temporary?
}

一些在 net 包中声明的函数会返回动态类型为 net.Errorerror 类型值。如果变量 err 的动态类型是 net.Error,可以根据它的 Temporary 方法的结果值来判断当前的错误状态是否临时的:

if netErr, ok := err.(net.Error); ok && netErr.Temporary(){
	// 省略若干语句
}

如果是临时的,那么就可以间隔一段时间之后再对之前的操作进行重试,否则就记录错误状态的信息并退出。

2.2 panic

Go语言内建的一个专用函数,目的使编程人员能够在自己的程序中报告运行期间的,不可恢复的错误状态。panic 函数被用于停止当前的控制流程的执行并报告一个运行时恐慌。它可以接受一个任意类型的参数值,这个参数常常是一个 string 类型值或者 error 类型值。例如:

package main

import (
	"errors"
)

func main(){
	outerFunc()
}

func outerFunc(){
	innerFunc()
}

func innerFunc(){
	panic(errors.New("A intended fatal error!"))
}

当在函数 innerFunc 中调用 panic 函数之后,函数 innerFunc 的执行会被停止。然后,流程控制权会被交回给函数 innerFunc 的调用方 outerFunc 函数,此时,outerFunc 函数的执行也将被停止。运行时恐慌就这样沿着调用栈反方向进行传达,直至到达当前 Goroutine(也被称为Go程,可以看作是一个能够独占一个系统线程并在其中运行程序的独立环境)调用栈的最顶层。这时,当前 Goroutine 的调用栈中的所有函数的执行都已经被停止了,意味着程序已经崩溃了。

运行时恐慌并不都是通过调用 panic 函数的方式引发的。它也可以由Go语言的运行时系统来引发。例如:

myIndex := 4
ia := [3]int{1, 2, 3}
_ = ia[myIndex] // 产生了一个数组访问越界的运行时错误,会引发一个运行时恐慌。

如上这个运行时恐慌由运行时系统报告的,它相当于显示地调用 panic 函数并传入一个 runtime.Error 类型的参数值,该类型的声明如下:

type Error interface {
	error 
	// RuntimeError is a no-op function but serves to distinguish types that are runtime errors
	// from ordinary errors: a type is a runtime error if it has a RuntimeError method.
	RuntimeError()
}

2.3 recover

运行时恐慌一旦被引发就会向调用方传递直至程序崩溃。Go语言提供了专用于“拦截”运行时恐慌的内建函数— recover。它可以使当前的程序从运行时恐慌的状态中恢复并重新获得流程控制权。recover 函数有一个 interface{} 类型的结果值,如果当前的程序正处于运行时恐慌的状态下,那么调用 recover 函数将会得到一个 非nilinterface{} 类型值。如果当时的运行时恐慌是由Go语言的运行时程序引发的,就会获得一个 runtime.Error 类型的值。

只有在 defer 语句的延迟函数中调用 recover 函数才能够起到“拦截”运行时恐慌的作用。例如:

defer func(){
	if r := recover(); r != nil {
		fmt.Printf("Recovered panic: %s\n", r)
	}
}()

再看如下一个示例,有助于理解 panic 函数、recover 函数和 defer 语句有关的运行机制。例如:

package main

import (
	"fmt"
)

func main(){
	fetchDemo()
	// 由于运行时恐慌在将要被继续传递给fetchDemo函数的调用方的时候被“拦截”。
	// 因此fetchDemo函数的调用方(也就是main函数)得以重获流程控制权,下一条语句可以打印
	fmt.Println("The main function is executed.") 
}

func fetchDemo() {
	defer func() {
		if v := recover(); v != nil {
			fmt.Printf("Recovered a panic. [index=%d]\n", v)
		}
	}()
	ss := []string{"A", "B", "C"}
	fmt.Printf("Fetch the elements in %v one by one...\n", ss)
	fetchElement(ss, 0)
	fmt.Println("The elements fetching is done.")//上面的语句出现了运行时恐慌,因此不会执行
}

func fetchElement(ss []string, index int) (element string) {
	if index >= len(ss) {
		fmt.Printf("Occur a panic![index=%d]\n", index)
		panic(index)
	}
	fmt.Printf("Fetching the element... [index=%d]\n", index)
	element = ss[index]
	defer fmt.Printf("The elements is \"%s\". [index=%d]\n", element, index)
	fetchElement(ss, index + 1)
	return
}

如上命令源码文件运行结果截图:

这里写图片描述

在Go语言标准库中,即使使用的某个程序实体的内部发生了运行时恐慌,这个运行时恐慌也会在被传递给我们编写的程序使用方之前被“平息”并以 error 类型值的形式返回给使用方。在这些标准库代码包中,往往都会有自己的 error 接口类型的实现。只有当调用 recover 函数得到的结果值的类型是它们自定义的 error 类型的实现类型的时候,才会去处理这个运行时恐慌,否则就会重新引发一个运行时恐慌(re-panic)并携带相同的值。

在标准库代码包 fmtscan.goToken 函数就是如下的这样处理运行时恐慌的。声明如下:

func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
	defer func() {
		if e := recover(); e != nil {
			if se, ok := e.(scanError); ok {
				err = se.err
			} else {
				panic(e)
			}
		}
	}()
	// 省略若干条语句
}

Token 函数包含的延迟函数中,当运行时恐慌携带的值的类型是 fmt.scanError 类型的时候,这个值就会被赋值给代表结果值的变量 err,否则运行时恐慌就会被重新引发。

一个运行时恐慌无论重新引发几次,它所有的引发信息都依然会被提供在最终的程序崩溃报告中。重新引发一个运行时恐慌的时候使用如下:

panic(e)

在使用Go语言编写程序时,在使用上面类似 Token 函数的惯用法之前应该明确和统一可以被立即处理和需要被重新引发的运行时恐慌的种类。一般情况下,如果携带的值是动态类型为 runtime.Errorerror 类型值的话,这个运行时恐慌就应该被重新引发。从运行时恐慌的分类和处理决策角度看,在必要时自行定义一些 error 类型的实现类型是有好处的。

建议: 对于运行时恐慌的引发,应该在遇到致命的、不可恢复的错误状态时才去引发一个运行时恐慌,否则,可以完全利用函数或方法的结果值来向程序使用方传达错误状态。另外,应该仅在程序模块的边界位置上的函数或方法中对运行时恐慌进行“拦截”和“平息”。

结语

本篇讲述了Go语言特殊流程控制方法 defererrorpanicrecover,下篇开始我们了解Go语言程序测试的相关内容,敬请期待!!!!

最后附上知名的Go语言开源框架:

Gobot: 一个非常有意思的开源项目。它旨在成为下一代自动机工程学框架。换句话说,我们可以用它来控制机器人!它已经支持了10个(或者更多)不同的硬件平台。这其中包括已经被国内的计算机硬件发烧友所熟知的 Arduino 。该开源项目的官方网址是 http://gobot.io

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go语言学习16-特殊流程控制 的相关文章

  • java 静态变量 静态代码初始化块 构造函数的顺序

    静态变量或静态代码块 先后与代码书写顺序有关 初始化代码块或普通变量 顺序与代码书写顺序有关 构造函数 几大原则 一 静态成员变量 Static 1 静态成员变量为类变量 所有对象共享同一内存空间 2 静态成员变量的声明和定义仅在首次加载类
  • Go语言学习19-样本测试

    样本测试 引言 样本测试 1 编写样本测试函数 2 样本测试的运行 3 样本测试函数的命名 结语 引言 上一篇笔者介绍了 Go 语言的 基准测试 其实在测试源码文件中还可以添加样本测试函数 但编写样本测试函数不需要使用 testing 代码
  • Vue中启动提示polyfill缺少-webpack v5版本导致

    安装 npm i node polyfill webpack plugin 因为我们的项目使用webpack v5 其中polyfill Node核心模块被删除 所以 我们安装它是为了在项目中访问这些模块 vue config js文件 c
  • 创建&删除Anaconda虚拟环境

    一 创建Anaconda虚拟环境 参考博主之前的blog https blog csdn net m0 51339444 article details 123715285 二 删除Anaconda虚拟环境 删除Anaconda虚拟环境不建
  • iptables Permission denied (you must be root)

    环境 Docker容器 操作 iptables L 现象 iptables v版本 can t initialize iptables table filter Permission denied you must be root 原因 i
  • JavaScript中defer的作用

    JavaScript中defer的作用 Javascript中defer的作用是文档加载完毕了再执行脚本 这样会避免找不到对象的问题 defer是脚本程序强大功能中的一个 无名英雄 它告诉浏览器Script段包含了无需立即执行的代码 并且
  • 项目问题总结

    1 android studio 导入开源项目源码时要注意与自己包的冲突 比如 你有一个com xxxx的包 而需要导入的是com xx yy 你就不能把整个包复制过来 否则会报can t resolve symbil 因为它根据com会到
  • MySQL之2003错误的解决方法

    今天装了MySQL 结果发现到了晚上打开Command Line Client是已输入密码就错误 然后出现一个error2003瞬间窗口关闭 后来找了一下发现是没有开启MySQL server的原因 解决方法 在命令行输入net start
  • SQLSTATE=08S01通讯连接失败

    导致此错误的原因是连接池已经耗尽 所以在连接数据库时要记得关闭连接 防止连接用完
  • ubuntu 16.04安装提示end Kernel panic

    问题描述 ubuntu 最新版本VM虚拟机安装时出现错误end kernel panic not syncing corrupted stack end detected inside scheduler的解决方式 解决办法 选择自定义安装
  • 【Rust】003-基础语法:流程控制

    Rust 003 基础语法 流程控制 文章目录 Rust 003 基础语法 流程控制 一 概述 二 if 表达式 1 语法格式 2 多个 3 获取表达式的值 三 循环 1 loop 无限循环 可跳出 无限循环 跳出循环 返回值 2 whil
  • Error[Pe147] in IAR

    系统 win10 IDE IAR MCU cc2530 Error Error Pe147 declaration is incompatible with banked func xdata reentrant void UartSend
  • Hibernate4与hibernate3主要区别与版本不一致导致的错误

    Hibernate版本改动 Hibernate4的改动较大只有spring3 1以上版本能够支持 Spring3 1取消了HibernateTemplate 因为Hibernate4的事务管理已经很好了 不用Spring再扩展了 这里简单介
  • RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.a += torch.o

    叶子节点不能执行in place 原地 操作 因为在进行前向传播的时候得到的是叶子结点的地址 再进行反向传播的时候这个地址不变才不会报错 地址改变了就会出错 要将 a torch ones 1 改为 a a torch ones 1 另外下
  • 项目启动卡在了Started Application in 10.266 seconds (JVM running for 13.033)

    好端端的项目启动后卡在这一行Started Application in 10 266 seconds JVM running for 13 033 日志中原本打印的执行的banner和程序都没有执行 访问那页面是404 很奇怪 因为啥东西
  • 揭开golang中defer的坑

    揭开golang中defer的坑 defer执行顺序 后进先出 是个栈 函数在执行最后的RET返回指令前 会先检查是否存在defer语句 如果有从栈中依次取出 匿名返回值在return执行时被声明然后给他赋值 所以并不会返回在defer中修
  • Go_异常处理

    Error 异常就是程序出现了不正常的情况 会导致程序非正常停止 而异常处理就是针对非正常停止的情况 给出异常时的处理方式 语法错误不算异常体系中 error是一个接口 作用是返回程序异常的信息 errors实现了error type er
  • yum出现Error downloading packages错误

    yum出现Error downloading packages错误 错误表现方式 yum可以list 可以clean cache 但是无法安装 错误提示 Downloading packages Error downloading pack
  • Tomcat中404/500 错误,自定义错误页面

    Tomcat中404 500 错误 自定义错误页面 当服务器出现404 500错误时候希望能够给用户友好的现实界面 只需要在项目的web xml中添加一些配置
  • 错误码:events.js:141 throw er; // Unhandled ‘error’ event—解决办法

    错误码 events js 141 throw er Unhandled error event 解决办法 具体错误信息 dev events js 141 throw er Unhandled error event Error list

随机推荐

  • jmeter自动调试系统接口配置流程

    1 起因 最近测试的同事需要用jmeter工具压测一下我们项目的接口 因为接口中有token或者加密登录密码等逻辑 有的地方需要从上一步接口中拿到结果作为下一步的参数 进行传递 因为涉及的有点麻烦 就帮测试看了下这个工具 顺便记录一下 帮助
  • Markdown 实现页内跳转

    Markdown 实现页内跳转 在使用 Markdown 做一些论文笔记或者说写文档时 通常会出现这样一种情况 我们在文档的某个地方定义了一个 t a b l e o
  • 数据库的多表查询操作-查询只选修了1门课程的学生,显示学号、姓名、课程名。

    文章目录 前言 一 建立数据库和表 二 数据库展示 2 查询只选修了1门课程的学生 显示学号 姓名 课程名 总结 前言 在我看来数据库真的是一个神奇的东西 不但里面的只是点很深刻 而且对于我们学习起来还是有一定的压力的 关于数据的知识 我感
  • 【CSAPP】背景知识-调用者保存寄存器与被调用者保存寄存器

    1 调用者保存与被调用者保存 函数A调用了函数B 寄存器rbx在函数B中被修改了 逻辑上 rbx内容在调用函数B的前后应该保持一致 解决这个问题有两个策略 1 在函数A在调用函数B之前提前保存寄存器 rbx的内容 执行完函数B之后再恢复 r
  • 顶会常见的 python matplotlib 双Y轴柱状图

    效果图 代码如下 import matplotlib pyplot as plt import numpy as np plt rc font family Times New Roman x data 1 2 3 ax data 16 2
  • flex布局flex取值以及align-self、align-content、align-items的区别

    flex取值 flex是 flex grow flex shrink flex basis 的缩写 flex取值 flex grow flex shrink flex basis 默认值 0 1 auto none 0 0 auto aut
  • libevent库学习(1)

    一 初识 1 libevent介绍 Libevent 是一个用C语言编写的 轻量级的开源高性能事件通知库 主要有以下几个亮点 事件驱动 event driven 高性能 轻量级 专注于网络 不如 ACE 那么臃肿庞大 源代码相当精炼 易读
  • 计算机efs加密,EFS加密

    EFS Encrypting File System 加密文件系统 是Windows 2000 XP所特有的一个实用功能 对于NTFS卷上的文件和数据 都可以直接被操作系统加密保存 在很大程度上提高了数据的安全性 EFS加密简介 语音 EF
  • SystemUI模块总结

    SystemUI模块总结 1 SystemUI路径 SystemUI被放在 framework base packages apps SystemUI 在该目录的二级目录src com android下可看到SystemUI和Keyguar
  • NPN三极管和PNP三极管的工作原理

    记录一下 方便以后翻阅 学嵌入式还是要懂些电路知识的 NPN型三极管 由两块N型半导体中间夹着一块P型半导体组成 也称晶体三极管 是电子电路中最重要的器件 三极管的主要功能是电流放大和开关作用 可以把微弱的电信号变成一定强度的信号 三极管一
  • windows系统下安装Nginx以及简单使用(详解)

    一 背景 Nginx是一个很强大的高性能Web和反向代理服务 也是一种轻量级的Web服务器 可以作为独立的服务器部署网站 应用非常广泛 特别是现在前后端分离的情况下 而在开发过程中 我们常常需要在window系统下使用Nginx作为Web服
  • python-gRPC

    文章目录 python gRPC 一 简介 1 1 gRPC 1 2 protobuf 二 windows 环境下安装protobuf 2 1 下载环境包 2 2 解压缩 配置文件 2 3 验证是否安装成功 三 简单实例 3 1新建包 3
  • SonarQube 跳过指定检查

    ps 我使用了下面的项目过滤来做 因为一个项目会有多个分支 只想对部分项目来做过滤某些规则 这个规则还是有些重要的 环境 演示环境参考前边的文章 SonarQube 扫描 Java 代码 步骤 我们已经扫描一个 Java 项目 有 6 个
  • 九种查找算法-红黑树

    红黑树 2 3查找树能保证在插入元素之后能保持树的平衡状态 最坏情况下即所有的子节点都是2 node 树的高度为lgn 从而保证了最坏情况下的时间复杂度 但是2 3树实现起来比较复杂 于是就有了一种简单实现2 3树的数据结构 即红黑树 Re
  • idea搜索快捷键总结大全

    Ctrl F 是在本页查找 Ctrl Shift R是全局查找 Ctrl Shift N按文件名搜索文件 ctrl alt 鼠标左键 跳转到接口实现类 alt 7展示类结构 参考文献 idea全局搜索快捷键总结 小志的博客的博客 CSDN博
  • 特征提取方法——共空间模式(Common Spatial Pattern,CSP)算法原理与实现

    一 原理 公共空间模式 CSP 算法采用监督的方法创建一个最优的公共空间滤波器 在最大化一类方差的同时最小化另一类方差 采用同时对角化两类任务协方差矩阵的方式 得到可区分程度最大的特征向量 适用于二分类任务的特征提取 公共空间模式 CSP
  • RocketMQ 消息积压排错

    文章目录 RocketMQ 消息积压排错 场景描述 排查步骤 总结 其它知识点 查看磁盘IO 查看IO TOP 查看节点网络状况 RocketMQ Console TPS的计算方式 RocketMQ 消息积压排错 场景描述 消息积压5000
  • 洛谷P1605迷宫问题——python

    题目描述 给定一个 N M N times M N M 方格的迷宫 迷宫里有 T T T 处障碍 障碍处不可通过 在迷宫中移动有上下左右四种方
  • 前端vue经典面试题78道(重点详细简洁)

    目录 1 自我介绍 2 vue面试题 1 v show和v if区别的区别 2 为何v for要用key 3 描述vue组件声明周期mm 单组件声明周期图 父子组件生命周期图 4 vue组件如何通信 5 描述组件渲染和更新的过程 1 vue
  • Go语言学习16-特殊流程控制

    特殊流程控制 引言 特殊流程控制 1 defer语句 2 异常处理 2 1 error 2 2 panic 2 3 recover 结语 引言 上一篇博文介绍了 Go 语言的基本流程控制 本篇我们介绍 Go 语言的特殊流程控制 特殊流程控制