回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,8 +1,8 @@
## 5.8. Deferred函
## 5.8. Deferred函
在findLinks的例子中用http.Get的出作html.Parse的入。有url的容的是HTML格式的html.Parse才可以正常工作實際url指向的容很富,可能是片,文本或是其他。將這些格式的內容傳遞給html.parse會産生不良果。
在findLinks的例子中用http.Get的出作html.Parse的入。有url的容的是HTML格式的html.Parse才可以正常工作实际url指向的容很富,可能是片,文本或是其他。将这些格式的内容传递给html.parse会产生不良果。
下面的例子取HTML頁面併輸出頁面的標題。title函數會檢査服務器返的Content-Type字段如果發現頁面不是HTML將終止函數運行,返迴錯誤
下面的例子取HTML页面并输出页面的标题。title函数会检查服务器返的Content-Type字段如果发现页面不是HTML将终止函数运行,返回错误
<u><i>gopl.io/ch5/title1</i></u>
```Go
@@ -32,7 +32,7 @@ func title(url string) error {
}
```
下面展示了行效果:
下面展示了行效果:
```
$ go build gopl.io/ch5/title1
@@ -44,11 +44,11 @@ $ ./title1 https://golang.org/doc/gopher/frontpage.png
title: https://golang.org/doc/gopher/frontpage.png has type image/png, not text/html
```
resp.Body.close調用了多次,這是爲了確保title在所有行路下(使函數運行失)都關閉了網絡連接。着函數變得複雜,需要理的錯誤也變多,維護清理邏輯變得越來越睏難。而Go語言獨有的defer機製可以事情變得簡單
resp.Body.close用了多次,这是为了确保title在所有行路下(使函数运行失)都关闭了网络连接。着函数变得复杂,需要理的错误也变多,维护清理逻辑变得越来越困难。而Go语言独有的defer机制可以事情变得简单
需要在調用普通函或方法前加上關鍵字defer就完成了defer所需要的法。defer句被執行時跟在defer面的函數會被延遲執行。直到包含defer句的函數執行完畢時defer的函數才會被執行,不包含defer句的函是通return正常束,是由panic致的異常結束。你可以在一個函數中執行多defer句,它們的執行順序與聲明順序相反。
需要在用普通函或方法前加上关键字defer就完成了defer所需要的法。defer句被执行时跟在defer面的函数会被延迟执行。直到包含defer句的函数执行完毕时defer的函数才会被执行,不包含defer句的函是通return正常束,是由panic致的异常结束。你可以在一个函数中执行多defer句,它们的执行顺序与声明顺序相反。
defer語句經常被用於處理成的操作,如打開、關閉、連接、斷開連接、加鎖、釋放鎖。通defer機製,不論函數邏輯多複雜,都能保在任何行路下,源被放。釋放資源的defer應該直接跟在請求資源的語句後。在下面的代中,一defer句替代了之前的所有resp.Body.Close
defer语句经常被用于处理成的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通defer机制,不论函数逻辑多复杂,都能保在任何行路下,源被放。释放资源的defer应该直接跟在请求资源的语句后。在下面的代中,一defer句替代了之前的所有resp.Body.Close
<u><i>gopl.io/ch5/title2</i></u>
```Go
@@ -71,7 +71,7 @@ func title(url string) error {
}
```
理其他資源時也可以采用defer機製,比如文件的操作:
理其他资源时也可以采用defer机制,比如文件的操作:
<u><i>io/ioutil</i></u>
```Go
@@ -86,7 +86,7 @@ func ReadFile(filename string) ([]byte, error) {
}
```
或是理互斥9.2章)
或是理互斥9.2章)
```Go
var mu sync.Mutex
@@ -98,7 +98,7 @@ func lookup(key string) int {
}
```
調試複雜程序defer機製也常被用於記録何時進入和退出函。下例中的bigSlowOperation函,直接調用trace記録函數的被調情況。bigSlowOperation被調時trace會返迴一個函數值,該函數值會在bigSlowOperation退出時被調用。通過這種方式, 我可以隻通過一條語句控製函數的入口和所有的出口,甚至可以記録函數的運行時間如例子中的start。需要意一:不要忘defer語句後的圓括號,否則本該在進入時執行的操作在退出時執行,而本在退出時執行的,永遠不會被執行。
调试复杂程序defer机制也常被用于记录何时进入和退出函。下例中的bigSlowOperation函,直接用trace记录函数的被调情况。bigSlowOperation被调时trace会返回一个函数值,该函数值会在bigSlowOperation退出时被调用。通过这种方式, 我可以只通过一条语句控制函数的入口和所有的出口,甚至可以记录函数的运行时间如例子中的start。需要意一:不要忘defer语句后的圆括号,否则本该在进入时执行的操作在退出时执行,而本在退出时执行的,永远不会被执行。
<u><i>gopl.io/ch5/trace</i></u>
```Go
@@ -118,7 +118,7 @@ func trace(msg string) func() {
}
```
每一次bigSlowOperation被調用,程序都會記録函數的進入,退出,持續時間。(我用time.Sleep模擬一個耗時的操作)
每一次bigSlowOperation被用,程序都会记录函数的进入,退出,持续时间。(我用time.Sleep模拟一个耗时的操作)
```
$ go build gopl.io/ch5/trace
@@ -127,9 +127,9 @@ $ ./trace
2015/11/18 09:53:36 exit bigSlowOperation (10.000589217s)
```
知道defer句中的函數會在return句更新返迴值變量後再執行,又因在函中定的匿名函可以訪問該函數包括返迴值變量在的所有量,所以,匿名函采用defer機製,可以使其察函的返值。
知道defer句中的函数会在return句更新返回值变量后再执行,又因在函中定的匿名函可以访问该函数包括返回值变量在的所有量,所以,匿名函采用defer机制,可以使其察函的返值。
以double函數爲例:
以double函数为例:
```Go
func double(x int) int {
@@ -137,7 +137,7 @@ func double(x int) int {
}
```
們隻需要首先命名double的返再增加defer句,我就可以在double每次被調用時,輸出參數以及返值。
们只需要首先命名double的返再增加defer句,我就可以在double每次被调用时,输出参数以及返值。
```Go
func double(x int) (result int) {
@@ -149,9 +149,9 @@ _ = double(4)
// "double(4) = 8"
```
可能doulbe函數過於簡單,看不出這個小技巧的作用,但對於有許多return句的函而言,這個技巧很有用。
可能doulbe函数过于简单,看不出这个小技巧的作用,但对于有许多return句的函而言,这个技巧很有用。
被延遲執行的匿名函甚至可以改函數返迴給調用者的返值:
被延迟执行的匿名函甚至可以改函数返回给调用者的返值:
```Go
func triple(x int) (result int) {
@@ -161,7 +161,7 @@ func triple(x int) (result int) {
fmt.Println(triple(4)) // "12"
```
在循環體中的defer句需要特别意,因爲隻有在函數執行完畢後,這些被延的函數才會執行。下面的代碼會導致繫統的文件描述符耗,因在所有文件都被理之前,有文件會被關閉
在循环体中的defer句需要特别意,因为只有在函数执行完毕后,这些被延的函数才会执行。下面的代码会导致系统的文件描述符耗,因在所有文件都被理之前,有文件会被关闭
```Go
for _, filename := range filenames {
@@ -175,7 +175,7 @@ for _, filename := range filenames {
}
```
種解決方法是將循環體中的defer句移至另外一個函數。在每次循環時,調用這個函數
种解决方法是将循环体中的defer句移至另外一个函数。在每次循环时,调用这个函数
```Go
for _, filename := range filenames {
@@ -193,7 +193,7 @@ func doFile(filename string) error {
}
```
下面的代是fetch1.5)的改版,我們將http響應信息入本地文件而不是從標準輸出流出。我們通過path.Base提出url路的最一段作文件名。
下面的代是fetch1.5)的改版,我们将http响应信息入本地文件而不是从标准输出流出。我们通过path.Base提出url路的最一段作文件名。
<u><i>gopl.io/ch5/fetch</i></u>
```Go
@@ -222,6 +222,6 @@ func fetch(url string) (filename string, n int64, err error) {
}
```
resp.Body.Close延遲調用我們已經見過了,在此不做解。上例中,通os.Create打文件進行寫入,在關閉文件,我們沒有對f.close采用defer機製,因爲這會産生一些微妙的錯誤。許多文件繫統尤其是NFS入文件時發生的錯誤會被延到文件關閉時反饋。如果沒有檢査文件關閉時的反信息,可能會導致數據丟失,而我們還誤以爲寫入操作成功。如果io.Copy和f.close都失了,我們傾向於將io.Copy的錯誤信息反饋給調用者,因它先f.close生,更有可能接近問題的本
resp.Body.Close延迟调用我们已经见过了,在此不做解。上例中,通os.Create打文件进行写入,在关闭文件,我们没有对f.close采用defer机制,因为这会产生一些微妙的错误。许多文件系统尤其是NFS入文件时发生的错误会被延到文件关闭时反馈。如果没有检查文件关闭时的反信息,可能会导致数据丢失,而我们还误以为写入操作成功。如果io.Copy和f.close都失了,我们倾向于将io.Copy的错误信息反馈给调用者,因它先f.close生,更有可能接近问题的本
**練習5.18**不改fetch的行,重fetch函要求使用defer機製關閉文件。
**练习5.18**不改fetch的行,重fetch函要求使用defer机制关闭文件。