城阳区建设银行网站,asp学校网站系统,中国林业工程建设协会网站,苏州公司注册代理记账什么是延期#xff1f;
Defer 语句用于在存在 defer 语句的周围函数返回之前执行函数调用。该定义可能看起来很复杂#xff0c;但通过示例就很容易理解。
例子
package mainimport ( fmt
)func finished() { fmt.Println(Finished finding largest
Defer 语句用于在存在 defer 语句的周围函数返回之前执行函数调用。该定义可能看起来很复杂但通过示例就很容易理解。
例子
package mainimport ( fmt
)func finished() { fmt.Println(Finished finding largest)
}func largest(nums []int) { defer finished() fmt.Println(Started finding largest)max : nums[0]for _, v : range nums {if v max {max v}}fmt.Println(Largest number in, nums, is, max)
}func main() { nums : []int{78, 109, 2, 563, 300}largest(nums)
}Run in playground
上面是一个简单的程序用于查找给定切片的最大数量。该largest函数接受一个int切片作为参数并打印该切片的最大数量。函数的第一行包含语句defer finished()。这意味着该finished()函数将在函数返回之前被调用。运行该程序您可以看到打印出以下输出。
Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest 函数开始执行并打印上述输出的前两行。在它返回之前我们的延迟函数finished会执行并打印文本Finished finding largest
延迟方法
Defer 不仅仅限于函数。Defer方法调用也是完全合法的。
让我们编写一个小程序来测试一下。
package mainimport ( fmt
)type person struct { firstName stringlastName string
}func (p person) fullName() { fmt.Printf(%s %s,p.firstName,p.lastName)
}func main() { p : person {firstName: John,lastName: Smith,}defer p.fullName()fmt.Printf(Welcome )
}Run in playground
在上面的程序中我们第21行调用了Defer。 程序的其余部分是不言自明的。该程序输出
Welcome John Smith 论据评价
延迟函数的参数在defer执行语句时计算而不是在实际函数调用完成时计算。
让我们通过一个例子来理解这一点。
package mainimport ( fmt
)func printA(a int) { fmt.Println(value of a in deferred function, a)
}
func main() { a : 5defer printA(a)a 10fmt.Println(value of a before deferred function call, a)}Run in playground
在上面的程序中a的值最初为5。 当第 12行执行 defer 语句时。a的值是5因此这将是被当做printA延迟函数的参数。我们将第 1 3行中的值更改a为 10。该程序输出
value of a before deferred function call 10
value of a in deferred function 5 从上面的输出可以理解虽然执行 defer 语句后的a值发生了变化10但实际的延迟函数调用printA(a)仍然打印5。
延迟堆栈
当一个函数有多个延迟调用时它们会被压入堆栈并按后进先出LIFO顺序执行。
我们将编写一个小程序使用延迟堆栈反向打印字符串。
package mainimport ( fmt
)func main() { name : Naveenfmt.Printf(Original String: %s\n, string(name))fmt.Printf(Reversed String: )for _, v : range name {defer fmt.Printf(%c, v)}
}Run in playground
在上面的程序中使用for range循环迭代字符串并调用defer fmt.Printf(%c, v)这些延迟调用将被添加到堆栈中。 上图表示添加 defer 调用后堆栈的内容。堆栈是后进先出的数据结构。最后压入堆栈的 defer 调用将首先被拉出并执行。在这种情况下defer fmt.Printf(%c, n)将首先执行因此字符串将以相反的顺序打印。
该程序将输出
Original String: Naveen
Reversed String: neevaN 延迟的实际使用
到目前为止我们看到的代码示例并未显示 defer 的实际用途。在本节中我们将研究 defer 的一些实际用途。
Defer 用于无论代码流如何都应该执行函数调用的地方。让我们通过使用WaitGroup的程序示例来理解这一点。我们将首先编写不使用 defer 的程序然后修改它以使用 defer 并了解 defer 有多么有用。
package mainimport ( fmtsync
)type rect struct { length intwidth int
}func (r rect) area(wg *sync.WaitGroup) { if r.length 0 {fmt.Printf(rect %vs length should be greater than zero\n, r)wg.Done()return}if r.width 0 {fmt.Printf(rect %vs width should be greater than zero\n, r)wg.Done()return}area : r.length * r.widthfmt.Printf(rect %vs area %d\n, r, area)wg.Done()
}func main() { var wg sync.WaitGroupr1 : rect{-67, 89}r2 : rect{5, -67}r3 : rect{8, 9}rects : []rect{r1, r2, r3}for _, v : range rects {wg.Add(1)go v.area(wg)}wg.Wait()fmt.Println(All go routines finished executing)
}Run in playground
在上面的程序中我们创建了一个rect结构体和area的方法。此方法检查矩形的长度和宽度是否小于零。如果是则打印相应的消息否则打印矩形的面积。
该main函数创建 3 个类型为rect的变量。然后将它们添加到rects 的切片中。 然后使用循环迭代该切片并在第 37 行中将area方法作为并发调用。37. WaitGroup用于确保 main 函数被阻塞直到所有 Goroutines 执行完毕。此 WaitGroup 作为参数传递给方法并且调用wg.Done()方法通知主函数 Goroutine 已完成其工作。如果您仔细观察您会发现调用*wg.Done()*发生在区域方法返回之前。
无论代码流采用的路径如何都应在方法返回之前调用 wg.Done()因此这些调用可以有效地由多个调用替换单个调用
在下面的程序中我们删除了wg.Done()上面程序中的 3 个调用并将其替换为defer wg.Done()的单个调用。这使得代码更加简单易懂。
package mainimport ( fmtsync
)type rect struct { length intwidth int
}func (r rect) area(wg *sync.WaitGroup) { defer wg.Done()if r.length 0 {fmt.Printf(rect %vs length should be greater than zero\n, r)return}if r.width 0 {fmt.Printf(rect %vs width should be greater than zero\n, r)return}area : r.length * r.widthfmt.Printf(rect %vs area %d\n, r, area)
}func main() { var wg sync.WaitGroupr1 : rect{-67, 89}r2 : rect{5, -67}r3 : rect{8, 9}rects : []rect{r1, r2, r3}for _, v : range rects {wg.Add(1)go v.area(wg)}wg.Wait()fmt.Println(All go routines finished executing)
}Run in playground
该程序输出
rect {8 9}s area 72
rect {-67 89}s length should be greater than zero
rect {5 -67}s width should be greater than zero
All go routines finished executing 在上面的程序中使用 defer 还有一个优点。假设我们area使用新if条件向该方法添加另一个返回路径。如果调用wg.Done()没有延迟我们必须小心并确保我们调用wg.Done()这个新的返回路径。但由于调用wg.Done()被Defer我们不必担心向该方法添加新的返回路径。
本教程到此结束。祝你有美好的一天。