From e3c558e546624aeca28854780a38be78d6fe6e47 Mon Sep 17 00:00:00 2001 From: "rick.chan" Date: Wed, 7 Jul 2021 13:37:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20Golang=20=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E5=AE=9E=E4=BE=8B=E8=AF=A6=E8=A7=A3.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: rick.chan --- ...语言字符串操作.md => Golang_字符串操作.md} | 2 +- .../{Go_文件操作.md => Golang_文件操作.md} | 2 +- .../Go/Basic/闭包/Golang_闭包实例详解.md | 347 ++++++++++++++++++ 3 files changed, 349 insertions(+), 2 deletions(-) rename Software/Development/Language/Go/Basic/{Go_语言字符串操作.md => Golang_字符串操作.md} (97%) rename Software/Development/Language/Go/Basic/{Go_文件操作.md => Golang_文件操作.md} (99%) create mode 100644 Software/Development/Language/Go/Basic/闭包/Golang_闭包实例详解.md diff --git a/Software/Development/Language/Go/Basic/Go_语言字符串操作.md b/Software/Development/Language/Go/Basic/Golang_字符串操作.md similarity index 97% rename from Software/Development/Language/Go/Basic/Go_语言字符串操作.md rename to Software/Development/Language/Go/Basic/Golang_字符串操作.md index 09f5050..f527259 100644 --- a/Software/Development/Language/Go/Basic/Go_语言字符串操作.md +++ b/Software/Development/Language/Go/Basic/Golang_字符串操作.md @@ -1,4 +1,4 @@ -# Go 语言字符串操作 +# Golang 字符串操作 ## 1.分割/拼接操作 diff --git a/Software/Development/Language/Go/Basic/Go_文件操作.md b/Software/Development/Language/Go/Basic/Golang_文件操作.md similarity index 99% rename from Software/Development/Language/Go/Basic/Go_文件操作.md rename to Software/Development/Language/Go/Basic/Golang_文件操作.md index 36debf7..5879d87 100644 --- a/Software/Development/Language/Go/Basic/Go_文件操作.md +++ b/Software/Development/Language/Go/Basic/Golang_文件操作.md @@ -1,4 +1,4 @@ -# Go 文件操作 +# Golang 文件操作 os.File 封装了所有文件相关的操作,File 是一个结构体,有很多方法。 diff --git a/Software/Development/Language/Go/Basic/闭包/Golang_闭包实例详解.md b/Software/Development/Language/Go/Basic/闭包/Golang_闭包实例详解.md new file mode 100644 index 0000000..42fcb05 --- /dev/null +++ b/Software/Development/Language/Go/Basic/闭包/Golang_闭包实例详解.md @@ -0,0 +1,347 @@ +# [Golang 闭包实例详解](https://blog.csdn.net/li_101357/article/details/80196650) + +## 1.闭包的概念 + +闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说是函数和其引用环境的组合体。闭包的概念可以直接百度或者 google 详细搜索详细,这里主要是通过几个实例来对闭包进行详解。 + +## 2.闭包实例详解 + +### 2.1.实例 1 + +```go +package main + +import "fmt" + +//函数片段 +func add(base int) func(int) int { + fmt.Printf("&base = %p\n", &base) //打印变量地址,可以看出来 内部函数时对外部传入参数的引用 + + f := func(i int) int { + fmt.Printf("&base(in f) = %p\n", &base) + base += i + return base + } + + return f +} + +//由 main 函数作为程序入口点启动 +func main() { + fmt.Println("-1.------") + t1 := add(10) + fmt.Println("") + fmt.Println(t1(1), t1(2)) + fmt.Println("---------") + + fmt.Println("\n-2.------") + t2 := add(100) + fmt.Println("") + fmt.Println(t2(1), t2(2)) + fmt.Println("---------") +} +``` + +程序执行结果如下所示: + +```bash +-1.------ +&base = 0xc000014120 + +&base(in f) = 0xc000014120 +&base(in f) = 0xc000014120 +11 13 +--------- + +-2.------ +&base = 0xc000014128 + +&base(in f) = 0xc000014128 +&base(in f) = 0xc000014128 +101 103 +--------- +``` + +根据程序的执行结果可以看出来,内部函数是对外部变量引用。 + +重点:延迟调用有些知识点有异曲同工的地方,函数体内某个变量作为 defer 匿名函数的参数,则在定义 defer 时已获得值拷贝,否则引用某个变量的地址(引用拷贝)。代码片段如下所示: + +```go +package main + +import "fmt" + +//由 main 函数作为程序入口点启动 +func main() { + x, y := 1, 2 + + defer func(a int) { + fmt.Println("defer x = ", a, ", defer y = ", y) //y为闭包引用 + }(x) //x值拷贝 调用时传入参数 + + x += 100 + y += 200 + + fmt.Println("x =", x, ", y =", y) +} +``` + +程序执行结果: + +```bash +x = 101 , y = 202 +defer x = 1 , defer y = 202 +``` + +多次注册延迟调用,相反顺序执行: + +```go +package main + +import "fmt" + +func main() { + for i := 0; i < 3; i++ { + //多次注册延迟调用,相反顺序执行 + defer func(a int) { + fmt.Println("defer a =", a) //闭包引用局部变量 + }(i) + + defer func() { + fmt.Println("defer i =", i) //闭包引用局部变量 + }() + + fmt.Println("i =", i) + } +} +``` + +程序执行结果: + +```bash +i = 0 +i = 1 +i = 2 +defer i = 3 +defer a = 2 +defer i = 3 +defer a = 1 +defer i = 3 +defer a = 0 +``` + +### 2.2.实例 2 + +返回多个内部函数,程序片段如下所示: + +```go +package main + +import "fmt" + +//返回加减函数,重点:内部函数时对外部变量的引用 +func calc(base int) (func(int) int, func(int) int) { + + fmt.Printf("%p\n", &base) + add := func(i int) int { + fmt.Printf("%p\n", &base) + base += i + return base + } + + sub := func(i int) int { + fmt.Printf("%p\n", &base) + base -= i + return base + } + + return add, sub +} + +//由 main 函数作为程序入口点启动 +func main() { + f1, f2 := calc(100) + + fmt.Println(f1(1), f2(2)) //执行顺序:f1 f2 println + fmt.Println(f1(3), f2(4)) + fmt.Println(f1(5), f2(6)) + fmt.Println(f1(7), f2(8)) +} +``` + +程序执行结果: + +```bash +0xc00018c000 +0xc00018c000 +0xc00018c000 +101 99 +0xc00018c000 +0xc00018c000 +102 98 +0xc00018c000 +0xc00018c000 +103 97 +0xc00018c000 +0xc00018c000 +104 96 +``` + +### 2.3.实例 3 + +涉及 goroutine 时的情况,程序片段如下所示: + +```go +package main + +import ( + "fmt" + "time" +) + +//由 main 函数作为程序入口点启动 +func main() { + + for i := 0; i < 5; i++ { + go func() { + fmt.Println(i) //i变量值也是引用.创建5个线程执行函数, for循环执行过程中可能执行完的时候,线程刚好处于i的某个值。 + }() + + } + time.Sleep(time.Second * 1) +} +``` + +程序运行结果: + +```bash +# Time 1 +5 +5 +5 +5 +5 +# Time 2 +5 +5 +5 +5 +0 +# Time 3 +3 +5 +5 +5 +5 +``` + +代码改进: + +```go +package main + +import ( + "fmt" + "time" +) + +//由 main 函数作为程序入口点启动 +func main() { + + ch := make(chan int, 1) + + for i := 0; i < 5; i++ { + go func() { + ch <- 1 + fmt.Println(i) + }() + + <-ch + } + time.Sleep(time.Second * 1) +} +``` + +程序执行结果: + +```bash +# Time 1 +0 +1 +2 +3 +4 +# Time 2 +0 +1 +2 +3 +4 +``` + +通过这三个示例的结果,闭包中的值是对源变量的引用。指向的是变量的当前值。 + +### 2.4.实例 4:闭包错误引起的死锁 + +最初使用 goroutine 的时候,曾遇到一个程序死锁的问题。思来想去看代码都觉得问题,最后发现是错误使用闭包导致的问题。 + +具体的代码如下所示: + +```go +package main + +import ( + "fmt" +) + +func main() { + //创建slice + cs := make([](chan int), 10) + for i := 0; i < len(cs); i++ { + cs[i] = make(chan int) + } + + // 此处 for 循环为问题代码块 + for i := range cs { + go func() { + cs[i] <- i //创建线程,但是i是引用外部变量,不一定等线程执行的时候就是当前i值 + }() + } + + for i := 0; i < len(cs); i++ { + t := <-cs[i] //读取值的时候,可能会出现一只阻塞的情况 + fmt.Println(t) + } +} +``` + +主要功能就是:创建10个线程执行函数,并向 channal 写入值。由于 goroutine 还没有开始,i 的值已经跑到了最大 9,使得这几个 goroutine 都取的 i=9 这个值,从而都向 cs[9] 发消息,导致执行 "t := <-cs[i]" 时,cs[0]、cs[1]、cs[2]… 都阻塞起来了,从而导致了死锁。 + +两种修改方法如下: + +```go +for i := range cs { + go func(index int) { + cs[index] <- index + }(i) +} +``` + +或 + +```go +ch := make(chan int) +for i := range cs { + go func() { + ch <- 1 + cs[i] <- i + }() + <-ch +} +``` + +## 3.外部参考资料 + +* +* +*