go的闭包和并发

本文介绍了Go语言中的闭包特性,展示了如何在函数外部访问内部变量。同时,详细探讨了Go的并发机制,包括通过通道(channel)共享内存而非共享内存通信,以及使用`sync.WaitGroup`确保并发任务完成。提到了无缓冲和有缓冲管道的区别,并解释了管道阻塞的概念。此外,还讨论了并发中的锁机制,如互斥锁(Mutex)在解决内存争抢问题上的应用。
摘要由CSDN通过智能技术生成

闭包

闭包为一种特殊的函数

其特殊的性质在于,利用闭包可以在函数外访问到函数内的局部变量。

package main

import (
    "fmt"
)

func a() func() int {
    i := 0
    b := func() int {
        i++
        fmt.Println(i)
        return i
    }
    return b
}

func main() {
    c := a()
    c()//1
    c()//2
    c()//3

    a() //不会输出i
}

在c:=a()语句结束的时候,系统为a中的匿名函数分配了地址,而在之后的调用中,他们操作同一个地址,如果将代码稍作修改,如下

func main() {
    c1 := a()
    c2 := a()
    c1()//1
    c1()//2
    c1()//3
    c2()//1
    a() //不会输出i
}

此时c1和c2操作的并不是一块地址

详见如下

(18条消息) 前端面试题:闭包_ღ故里᭄ꦿ࿐的博客-CSDN博客_前端面试题闭包

并发

go的并发特性’

通过通道来共享内存 而不是通过共享内存来传递信息

func f(){
    fmt.print("hello")
}
func main(){
    go f()
    fmt.print("world")
}

go f()即重新申请一个goroutine

打印的信息应该为

world

因为启动一个goroutine需要时间,而当main函数结束的时候,整个程序结束,往往无法打印出"hello",而如果想要打印出"hello",则可以在程序结尾加一个time.sleep(time.second)来使主程序休眠一秒以等待hello的打印

而如果每一次都要在程序结尾加一个time.sleep()太过生硬,也会浪费太多时间,1s对一个程序来说太久了,而不管你的时间把控的多么好,你总要面临go并发还没有结束,主程序就已经结束的风险,或者牺牲时间来保证程序的正常运行,还好,go的设计并不是这么的不人性化

var wg sync.WaitGroup

func hello() {
    defer wg.Done()
    fmt.Println("Hello Goroutine!")
}
func main() {
    wg.Add(1)
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
    wg.Wait()
}

这样,当程序检测到并发任务已经全部完成,就会自动结束,确实很人性化。需要注意sync.WaitGroup是一个结构体,传递的时候要传递指针。

而如果只是进行这样简单的在打印hello world,做到这里确实足够了,你可以快速的打印出一个完整的hello world,而在实际开发中,我们往往利用并发来进行计算或者后端数据的处理,当处理结束之后,将处理结果或计算结果传回同意再处理,这才是go高并发的魅力!想想看,当一件庞大的事情交给1000个人来算,在将他们算出的结果进行处理,我们可以省去多少时间,但是问题在于,我们如何传递信息,与人而言,人可以通过语言来传递他们计算的结果,而goroutine也有他们的语言,就是

channel

var 变量 chan 元素类型

通道需要声明之后再使用

make(chan 元素类型,[缓冲大小])

缓冲大小是可选的,而是否选择缓冲大小则决定了该通道的,类型,这个类型在后面有大用,但是我们先来了解一下channel 的基本用法

通道有发送(send)、接收(receive)和关闭(close)三种操作。

ch := make(chan int)
ch<-10//接收
var num=<-ch//发送
close(ch)//关闭

就是这么简单,我们来运行下面一个程序

for {
		ch := make(chan int)
		go func() {
			ch <- 10
			fmt.Println("send")
		}()
		a := <-ch
		fmt.Println("receive", a)
		time.Sleep(time.Second)
	}

观察运行结果,很奇怪,明明只有main里面是有sleep 的,为什么另外一个他们的打印总是成对出现的,不应该是大量的”send“夹杂着一些"receive10"吗?

原因是因为管道的阻塞

管道的类型成为无缓冲的管道和有缓冲的管道(还有单向通道)

上文中我们使用的是无缓冲的管道,当我发送信息的时候,我必须等待管道内的信息先被接收,保证管道的整洁,同理,我接受信息的时候,管道内必须要有信息,否则我必须要等待

而另一种有缓冲的管道,则相当于一个柜子,我们可以把信息缓存在里面,等别人待会来取

make(chan 变量类型,[缓冲大小]) 不如用一个有缓冲的管道改良上面的代码试试把,单向通道大家可以自己去了解,如果有需要可以联系我补充

var x int64
var wg sync.WaitGroup

func add() {
    for i := 0; i < 5000; i++ {
        x = x + 1
    }
    wg.Done()
}
func main() {
    wg.Add(2)
    go add()
    go add()
    wg.Wait()
    fmt.Println(x)
} 

并发还有一大特性,就是锁,因为连个协程是相互独立的,所以当他们对一块地址进行操作的时候,就会发生争抢,运行上面代码,我们想得到的应该是10000,但是运行结果确实随机的,因为发生了内存的争抢,而为了避免这样的错误,来引入一个新的概念,改良代码如下

var x int64
var wg sync.WaitGroup
var lock sync.Mutex

func add() {
    for i := 0; i < 5000; i++ {
        lock.Lock() // 加锁
        x = x + 1
        lock.Unlock() // 解锁
    }
    wg.Done()
}
func main() {
    wg.Add(2)
    go add()
    go add()
    wg.Wait()
    fmt.Println(x)
} 

这种锁是互斥锁,当一个协程lock了以后,别的协程就无法访问,直到开锁

而除了互斥锁还存在一种读写锁,即当你为内存加上读锁之后,其它协程依然可以进行写的操作,而当你进行写锁,那无论都还是写都必须等待

闭包函数是指内部函数可以访问外部函数的局部变量,并且在外部函数执行结束后,依然可以访问和修改这些变量。在Go语言中,函数也可以作为返回值返回,形成闭包闭包函数可以用来实现函数式编程的一些特性。 函数式编程是一种编程范式,它将计算过程看作是一系列函数的应用。在函数式编程中,函数被视为一等公民,可以像其他数据类型一样传递、赋值和返回。函数式编程强调使用纯函数,即不依赖于外部状态和副作用的函数,这样可以提高代码的可读性、可维护性和并发性。 在Go语言中,闭包函数和函数式编程可以结合使用。通过闭包函数,我们可以在函数内部定义其他函数,并且这些内部函数可以访问外部函数的局部变量。这样就可以实现一些函数式编程的特性,例如高阶函数、柯里化和函数组合等。 以下是一个示例代码,演示了Go语言闭包函数和函数式编程的用法: ```go package main import "fmt" // 返回一个闭包函数,用于累加计数 func counter() func() int { count := 0 return func() int { count++ return count } } // 使用高阶函数将一个整数加倍 func double(n int) int { return n * 2 } // 使用高阶函数将两个整数相加 func add(a, b int) int { return a + b } func main() { // 使用闭包函数创建一个计数器 c := counter() // 调用计数器函数,并打印结果 fmt.Println(c()) // 输出: 1 fmt.Println(c()) // 输出: 2 // 使用高阶函数将整数加倍,并打印结果 fmt.Println(double(5)) // 输出: 10 // 使用高阶函数将两个整数相加,并打印结果 fmt.Println(add(3, 4)) // 输出: 7 } ``` 在上面的示例中,`counter`函数返回了一个闭包函数,用于实现简单的计数器。`double`函数和`add`函数都是高阶函数,可以作为参数传递给其他函数使用。这些示例展示了Go语言闭包函数和函数式编程的一些用法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值