当前位置: 首页 > news >正文

小型创意电子产品设计seo优化入门教程

小型创意电子产品设计,seo优化入门教程,python可以做网站后台吗,响应式设计网站案例文章目录 1. Channel 与 Goroutine 泄露1.1 发送不接收1.2 接收不发送1.3 nil channel2. 跳出 for-switch 或 for-select 3.for 迭代变量3.1 闭包中的for迭代变量3.2 for range 迭代变量 4. 循环内的 defer5.defer 函数的参数值6.nil interface 和 nil interface 值7.结构体指针…

文章目录

  • 1. Channel 与 Goroutine 泄露
    • 1.1 发送不接收
    • 1.2 接收不发送
    • 1.3 nil channel
    • 2. 跳出 for-switch 或 for-select
  • 3.for 迭代变量
    • 3.1 闭包中的for迭代变量
    • 3.2 for range 迭代变量
  • 4. 循环内的 defer
  • 5.defer 函数的参数值
  • 6.nil interface 和 nil interface 值
  • 7.结构体指针访问属性前先判空
  • 8.读取有顺序需要的不能使用map结构
  • 参考文献

本文将介绍 Golang 初学者容易菜的坑,希望广告 Gopher 避而远之。

1. Channel 与 Goroutine 泄露

当 channel 不恰当使用时,就可能导致 Goroutine 发生永久阻塞从而造成资源泄露。

先看一下 channel 不同状态下的读写与 close 操作的结果。

操作未关闭已关闭nil
发数据阻塞或成功发送panic永久阻塞
取数据阻塞或成功接收成功接收或零值永久阻塞
关闭成功关闭panicpanic

1.1 发送不接收

对于一个已满的 channel(buffered channel 容量已满或是 unbuffered channel),继续向 其发送数据将会导致当前goroutine阻塞。为了避免这种情况需要使用其他机制通知发送者。

// 错误示例
func produce() <-chan int {ch := make(chan int)go func() {defer close(ch)for i := 0; i < 10; i++ {ch <- i}}()return ch
}func main() {ch := produce()for num := range ch {if num == 2 {// 不想接收了,直接退出吧break}fmt.Println(num)}// 虽然此段代码能正常运行,但// produce产生goroutine将永远// 阻塞于 ch <- i上,造成资源泄露
}// 修正
func produce(doneCh chan struct{}) <-chan int {ch := make(chan int)go func() {defer close(ch)loop:for i := 0; i < 10; i++ {select {case ch <- i:case <-doneCh:break loop}}}()return ch
}
func main() {doneCh := make(chan struct{})ch := produce(doneCh)for num := range ch {if num == 2 {// 不想接收了,先通知一下生产者close(doneCh)break} fmt.Println(num)}
}

1.2 接收不发送

与前述情况相反,若接收者一直在一个不会再产生数据的 channel 上等待,将导致其所在routine 阻塞而泄露。 在Go中从一个 closed channel 读取数据:

  • 不会阻塞且获取对应类型的零值
  • for-range将退出
  • v, ok := <-ch中ok将为false
    所以可以利用上述性质通知接收方结束数据读取。
// 错误示例
func produce(doneCh chan struct{}) <-chan int {ch := make(chan int)go func() {select {case ch<-1:case <-doneCh:break}// 任务完成,直接退出}()return ch
}
func main() {doneCh := make(chan struct{})ch := produce(doneCh)for num := range ch {fmt.Println(num)}close(doneCh)// Output:// 1// fatal error: all goroutines are asleep - deadlock!
}// 修正
func produce(doneCh chan struct{}) <-chan int {ch := make(chan int)go func() {// 退出前先关闭channel防止有routine阻塞在上面defer close(ch)select {case ch<-1:case <-doneCh:break}}()return ch
}
func main() {doneCh := make(chan struct{})ch := produce(doneCh)for num := range ch {fmt.Println(num)}close(doneCh)// Output:// 1
}

1.3 nil channel

向 nil channel 发送和接收数据都将会导致阻塞。这种情况可能在我们定义 channel 时忘记初始化的时候发生。

func main() {defer func() {time.Sleep(time.Second)fmt.Println("num of routines: ", runtime.NumGoroutine())}()var ch chan intgo func() {<-ch// ch<-}()
}

2. 跳出 for-switch 或 for-select

没有指定标签的 break 只会跳出 switch/select 语句, 若不能使用 return 语句跳出的话,可为 break 跳出标签指定的代码块。

注意 goto 虽然也能跳转到指定位置,但依旧会再次进入 for-switch,死循环。

// break 配合 label 跳出指定代码块
func main() {
loop:for {switch {case true:fmt.Println("breaking out...")// break    // 死循环,一直打印 breaking out...break loop}}fmt.Println("out...")
}

3.for 迭代变量

3.1 闭包中的for迭代变量

for 语句中的迭代变量在每次迭代中都会重用,即 for 中创建的闭包函数 接收到的参数始终是同一个变量,所以在 goroutine 开始执行时都会得到同一个迭代值:

// 错误示例
func main() {n := 2wg := sync.WaitGroup{}wg.Add(n)for i := 0; i < n; i++ {go func() {defer wg.Done()fmt.Print(i)}()}wg.Wait()// Output:// 22
}// 修正
func main() {n := 2wg := sync.WaitGroup{}wg.Add(n)for i := 0; i < n; i++ {num := igo func() {defer wg.Done()fmt.Print(num)}()/*当然也可以这样go func(num int) {defer wg.Done()fmt.Println(num)}(i)*/}wg.Wait()// Output:// 01 或 10 
}

3.2 for range 迭代变量

for range 循环中迭代变量的短声明只会在开始时执行一次,后面都是直接赋值,所以迭代变量的变量地址是不变的,避免将其赋值给指针。

// 错误示例
slice1 := []int32{1, 2, 3, 4, 5}
slice2 := make([]*int32, len(slice1))
for i, item := range slice1 {slice2[i] = &item
}
for _, item := range slice2 {fmt.Printf("%v", *item)
}
// 55555// 修正
func Int32(v int32) *int32 {return &v
}
func main() {slice1 := []int32{1, 2, 3, 4, 5}slice2 := make([]*int32, len(slice1))for i, item := range slice1 {slice2[i] = Int32(item)}for _, item := range slice2 {fmt.Printf("%v", *item)}// 12345
}

4. 循环内的 defer

对 defer 延迟执行的函数,会在调用它的函数结束时执行,而不是在调用它的语句块结束时执行,注意区分开。

// 错误示例
type Resource struct {/*内部有一些需要释放的内容 */
}func (r Resource) Destroy() { /*...*/ }func getResource() Resource { /*...*/ }func main() {for i := 0; i < 10000; i++ {res := getResource()defer res.Destroy()// 会一直延迟至main结束才会释放// do something}
}// 修正
type Resource struct { /* 内部有一些需要释放的内容 */ 
}func (r Resource) Destroy() { /*...*/ }func getResource() Resource { /*...*/ }func main() {for i := 0; i < 10000; i++ {func () {res := getResource()defer res.Destroy()// 下次循环前就会释放,当然你也可以在最后直接调用Destroy// do something}()}
}

5.defer 函数的参数值

defer 只会延迟其后函数的执行,而不会延迟函数的参数的求值,若希望延迟其参数 求值,通常会加上一层匿名函数。

func main() {var i = 1times := func(num int) int {return num * 2}defer fmt.Println("resultA: ", times(i))defer func() {fmt.Println("resultB: ", func() int { return i * 2 }())}()i++// Output://  resultB: 4//  resultA: 2
}

6.nil interface 和 nil interface 值

Golang 中 interface 类型变量的实现中包含值与类型,只有两者都为 nil 时该变量才为nil。

// 错误示例
type Foo interface {Bar()
}type FooImpl struct {num int
}func (f *FooImpl) Bar() { fmt.Println(f.num) }func GenFoo(num int) (Foo, error) {var f *FooImplif num != 0 {f = &FooImpl{num}}return f, nil
}func main() {f, _ := GenFoo(0)// this comparison is never trueif f == nil {return}// Panicf.Bar()
}// 正确示例
func GenFoo(num int) (Foo, error) {if num != 0 {f := &FooImpl{num}return f, nil}return nil, errors.New("num is zero")
}

那么如何判断 interface{} 的值是否为 nil 呢?

func IsNil(i interface{}) {if i != nil {if reflect.ValueOf(i).IsNil() {fmt.Println("i is nil")return}fmt.Println("i isn't nil")}fmt.Println("i is nil")
}

7.结构体指针访问属性前先判空

当结构体指针为nil时,直接访问结构体属性会报空指针

// 错误示例
type Struct1 struct {id int32
}
func main() {var a *Struct1//panic: runtime error: invalid memory address or nil pointer dereferencea.id = 1
}// 修正
type Struct1 struct {id int32
}
func main() {var a *Struct1if a != nil {a.id = 1}
}

8.读取有顺序需要的不能使用map结构

Go 里面的map存储是无序的,for循环读取与写入的顺序并不同,需要排序的功能不能使用map,而需要使用slice。

// map 读取情况
intMap := make(map[int]int, 10)for i := 0; i < 10; i++ {intMap[i] = i
}for _, v := range intMap {fmt.Println(v)
}
//9
//3
//7
//……
//没有按照写入顺序输出,乱序的// slice 读取情况 
intSlice := make([]int, 0, 10)for i := 0; i < 10; i++ {intSlice = append(intSlice, i)
}for _, v := range intSlice {fmt.Println(v)
}
//0
//1
//2
//...
//读取是有序的

参考文献

Go 神坑 1 —— interface{} 与 nil 的比较 - CSDN
50 Shades of Go: Traps, Gotchas, and Common Mistakes
50 Shades of Go: Traps, Gotchas, and Common Mistakes中文翻译
如何防止 goroutine 泄露

http://www.hkea.cn/news/307763/

相关文章:

  • 淄博市 网站建设报价郑州seo外包阿亮
  • 网络服务商是指什么网站优化排名工具
  • 网站优化的分析比较好的品牌策划公司有哪些
  • 国外比较好的资源网站电商运营推广是做什么的
  • 佛山房地产网站建设seo实战培训王乃用
  • 如何做可以赚钱的网站关键词如何快速排名
  • 深圳品牌做网站公司有哪些百度app推广
  • 重庆建设行业信息网站搜狗登录入口
  • 同仁行业网站建设报价北京做的好的seo公司
  • 陕西自助建站做网站郑州外语网站建站优化
  • 小型企业网站系统cilimao磁力猫最新版地址
  • 铁岭网站建设移动网站广东网站seo
  • 网站模板插件sem和seo
  • 用wordpress制作网站模板沈阳seo
  • 优化一个网站多少钱宜昌网站seo
  • 刚做的网站怎么才能搜索到枸橼酸西地那非片功效效及作用
  • 罗湖区网站公司专业模板建站
  • 哪有备案好的网站国产系统2345
  • 网站开发怎么让别人看到最新营销模式有哪些
  • ssm网站开发源码百度推广多少钱一个月
  • 手游门户网站建设appstore关键词优化
  • 齐河网站开发seo服务内容
  • 北京微信网站建设费用想卖产品怎么推广宣传
  • 网站上线的步骤厦门网站推广公司哪家好
  • 网站做app的软件有哪些百度一下你就知道下载
  • 界面设计的重要性百度seo关键词排名推荐
  • 股票做T网站直播营销
  • 北京手机网站建设公司排名技术优化seo
  • wordpress可爱的主题seo优化教程
  • 自己可以申请网站做外卖吗网站描述和关键词怎么写