mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-11-04 13:33:41 +00:00
commit
58789561c8
@ -1,10 +1,10 @@
|
|||||||
## 12.1. 爲何需要反射?
|
## 12.1. 爲何需要反射?
|
||||||
|
|
||||||
有時候我們需要編寫一個函數能夠處理一類併不滿足普通公共接口的類型的值,也可能是因为它們併沒有確定的表示方式,或者是在我們設計該函數的時候還這些類型可能還不存在,各種情況都有可能。
|
有時候我們需要編寫一個函數能夠處理一類併不滿足普通公共接口的類型的值,也可能是因爲它們併沒有確定的表示方式,或者是在我們設計該函數的時候還這些類型可能還不存在,各種情況都有可能。
|
||||||
|
|
||||||
一個大家熟悉的例子是fmt.Fprintf函數提供的字符串格式化處理邏輯,它可以用例對任意類型的值格式化并打印,甚至支持用戶自定義的類型。讓我們也來嚐試實現一個類似功能的函數。为了簡單起見,我們的函數隻接收一個參數,然後返迴和fmt.Sprint類似的格式化後的字符串。我們实现的函數名也叫Sprint。
|
一個大家熟悉的例子是fmt.Fprintf函數提供的字符串格式化處理邏輯,它可以用例對任意類型的值格式化併打印,甚至支持用戶自定義的類型。讓我們也來嚐試實現一個類似功能的函數。爲了簡單起見,我們的函數隻接收一個參數,然後返迴和fmt.Sprint類似的格式化後的字符串。我們實現的函數名也叫Sprint。
|
||||||
|
|
||||||
我們使用了switch类型分支首先來測試輸入參數是否實現了String方法,如果是的話就使用該方法。然後繼續增加类型測試分支,檢査是否是每個基於string、int、bool等基礎類型的動態類型,併在每種情況下執行相应的格式化操作。
|
我們使用了switch類型分支首先來測試輸入參數是否實現了String方法,如果是的話就使用該方法。然後繼續增加類型測試分支,檢査是否是每個基於string、int、bool等基礎類型的動態類型,併在每種情況下執行相應的格式化操作。
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
func Sprint(x interface{}) string {
|
func Sprint(x interface{}) string {
|
||||||
@ -31,6 +31,6 @@ func Sprint(x interface{}) string {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
但是我們如何處理其它類似[]float64、map[string][]string等類型呢?我們當然可以添加更多的測試分支,但是這些組合類型的數目基本是無窮的。還有如何處理url.Values等命名的類型呢?雖然類型分支可以識别出底層的基礎類型是map[string][]string,但是它併不匹配url.Values類型,因爲它们是兩種不同的類型,而且switch类型分支也不可能包含每個類似url.Values的類型,這會導致對這些庫的循环依賴。
|
但是我們如何處理其它類似[]float64、map[string][]string等類型呢?我們當然可以添加更多的測試分支,但是這些組合類型的數目基本是無窮的。還有如何處理url.Values等命名的類型呢?雖然類型分支可以識别出底層的基礎類型是map[string][]string,但是它併不匹配url.Values類型,因爲它們是兩種不同的類型,而且switch類型分支也不可能包含每個類似url.Values的類型,這會導致對這些庫的循環依賴。
|
||||||
|
|
||||||
沒有一種方法來檢査未知類型的表示方式,我們被卡住了。這就是我們爲何需要反射的原因。
|
沒有一種方法來檢査未知類型的表示方式,我們被卡住了。這就是我們爲何需要反射的原因。
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# 第十二章 反射
|
# 第十二章 反射
|
||||||
|
|
||||||
Go语音提供了一種機製在運行時更新變量和檢査它們的值、調用它們的方法和它們支持的內在操作,但是在編譯時併不知道這些變量的具体類型。這種機製被稱爲反射。反射也可以讓我們將類型本身作爲第一類的值類型處理。
|
Go語音提供了一種機製在運行時更新變量和檢査它們的值、調用它們的方法和它們支持的內在操作,但是在編譯時併不知道這些變量的具體類型。這種機製被稱爲反射。反射也可以讓我們將類型本身作爲第一類的值類型處理。
|
||||||
|
|
||||||
在本章,我們將探討Go語言的反射特性,看看它可以給語言增加哪些表達力,以及在兩個至關重要的API是如何用反射機製的:一個是fmt包提供的字符串格式功能,另一個是類似encoding/json和encoding/xml提供的針對特定協議的編解碼功能。對於我們在4.6節中看到過的text/template和html/template包,它們的實現也是依賴反射技術的。然後,反射是一個複雜的內省技術,不應該隨意使用,因此,盡管上面這些包内部都是用反射技術實現的,但是它們自己的API都沒有公開反射相關的接口。
|
在本章,我們將探討Go語言的反射特性,看看它可以給語言增加哪些表達力,以及在兩個至關重要的API是如何用反射機製的:一個是fmt包提供的字符串格式功能,另一個是類似encoding/json和encoding/xml提供的針對特定協議的編解碼功能。對於我們在4.6節中看到過的text/template和html/template包,它們的實現也是依賴反射技術的。然後,反射是一個複雜的內省技術,不應該隨意使用,因此,盡管上面這些包內部都是用反射技術實現的,但是它們自己的API都沒有公開反射相關的接口。
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## 8.1. Goroutines
|
## 8.1. Goroutines
|
||||||
|
|
||||||
在Go語言中,每一個併發的執行單元叫作一個goroutine。設想這里有一個程序有兩個函數,一個函數做一些計算,另一個輸出一些結果,假設兩個函數沒有相互之間的調用關繫。一個線性的程序會先調用其中的一個函數,然後再調用來一個,但如果是在有兩個甚至更多個goroutine的程序中,對兩個函數的調用就可以在同一時間。我們馬上就會看到這樣的一個程序。
|
在Go語言中,每一個併發的執行單元叫作一個goroutine。設想這里的一個程序有兩個函數,一個函數做計算,另一個輸出結果,假設兩個函數沒有相互之間的調用關繫。一個線性的程序會先調用其中的一個函數,然後再調用另一個。如果程序中包含多個goroutine,對兩個函數的調用則可能發生在同一時刻。馬上就會看到這樣的一個程序。
|
||||||
|
|
||||||
如果你使用過操作繫統或者其它語言提供的線程,那麽你可以簡單地把goroutine類比作一個線程,這樣你就可以寫出一些正確的程序了。goroutine和線程的本質區别會在9.8節中講。
|
如果你使用過操作繫統或者其它語言提供的線程,那麽你可以簡單地把goroutine類比作一個線程,這樣你就可以寫出一些正確的程序了。goroutine和線程的本質區别會在9.8節中講。
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ f() // call f(); wait for it to return
|
|||||||
go f() // create a new goroutine that calls f(); don't wait
|
go f() // create a new goroutine that calls f(); don't wait
|
||||||
```
|
```
|
||||||
|
|
||||||
在下面的例子中,main goroutine會計算第45個菲波那契數。由於計算函數使用了效率非常低的遞歸,所以會運行相當可觀的一段時間,在這期間我們想要讓用戶看到一個可見的標識來表明程序依然在正常運行,所以顯示一個動畵的小圖標:
|
下面的例子,main goroutine將計算菲波那契數列的第45個元素值。由於計算函數使用低效的遞歸,所以會運行相當長時間,在此期間我們想讓用戶看到一個可見的標識來表明程序依然在正常運行,所以來做一個動畵的小圖標:
|
||||||
|
|
||||||
<u><i>gopl.io/ch8/spinner</i><u>
|
<u><i>gopl.io/ch8/spinner</i><u>
|
||||||
```go
|
```go
|
||||||
@ -45,6 +45,6 @@ func fib(x int) int {
|
|||||||
Fibonacci(45) = 1134903170
|
Fibonacci(45) = 1134903170
|
||||||
```
|
```
|
||||||
|
|
||||||
然後主函數返迴。當主函數返迴時,所有的goroutine都會直接打斷,程序退出。除了從主函數退出或者直接退出程序之外,沒有其它的編程方法能夠讓一個goroutine來打斷另一個的執行,但是我們之後可以看到,可以通過goroutine之間的通信來讓一個goroutine請求請求其它的goroutine,併讓其自己結束執行。
|
然後主函數返迴。主函數返迴時,所有的goroutine都會被直接打斷,程序退出。除了從主函數退出或者直接終止程序之外,沒有其它的編程方法能夠讓一個goroutine來打斷另一個的執行,但是之後可以看到一種方式來實現這個目的,通過goroutine之間的通信來讓一個goroutine請求其它的goroutine,併被請求的goroutine自行結束執行。
|
||||||
|
|
||||||
註意這里的兩個獨立的單元是如何進行組合的,spinning和菲波那契的計算。每一個都是寫在獨立的函數中,但是每一個函數都會併發地執行。
|
留意一下這里的兩個獨立的單元是如何進行組合的,spinning和菲波那契的計算。分别在獨立的函數中,但兩個函數會同時執行。
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# 第八章 Goroutines和Channels
|
# 第八章 Goroutines和Channels
|
||||||
|
|
||||||
併發程序指的是同時做好幾件事情的程序,隨着硬件的發展,併發程序顯得越來越重要。Web服務器會一次處理成韆上萬的請求。平闆電腦和手機app在渲染用戶動畵的同時,還會後台執行各種計算任務和網絡請求。卽使是傳統的批處理問題--讀取數據,計算,寫輸出--現在也會用併發來隱藏掉I/O的操作延遲充分利用現代計算機設備的多核,盡管計算機的性能每年都在增長,但併不是線性。
|
併發程序指同時進行多個任務的程序,隨着硬件的發展,併發程序變得越來越重要。Web服務器會一次處理成韆上萬的請求。平闆電腦和手機app在渲染用戶畵面同時還會後台執行各種計算任務和網絡請求。卽使是傳統的批處理問題--讀取數據,計算,寫輸出--現在也會用併發來隱藏掉I/O的操作延遲以充分利用現代計算機設備的多個核心。計算機的性能每年都在以非線性的速度增長。
|
||||||
|
|
||||||
Go語言中的併發程序可以用兩種手段來實現。這一章會講解goroutine和channel,其支持“順序進程通信”(communicating sequential processes)或被簡稱爲CSP。CSP是一個現代的併發編程模型,在這種編程模型中值會在不同的運行實例(goroutine)中傳遞,盡管大多數情況下被限製在單一實例中。第9章會覆蓋到更爲傳統的併發模型:多線程共享內存,如果你在其它的主流語言中寫過併發程序的話可能會更熟悉一些。第9章同時會講一些本章不會深入的併發程序帶來的重要風險和陷阱。
|
Go語言中的併發程序可以用兩種手段來實現。本章講解goroutine和channel,其支持“順序通信進程”(communicating sequential processes)或被簡稱爲CSP。CSP是一種現代的併發編程模型,在這種編程模型中值會在不同的運行實例(goroutine)中傳遞,盡管大多數情況下仍然是被限製在單一實例中。第9章覆蓋更爲傳統的併發模型:多線程共享內存,如果你在其它的主流語言中寫過併發程序的話可能會更熟悉一些。第9章也會深入介紹一些併發程序帶來的風險和陷阱。
|
||||||
|
|
||||||
盡管Go對併發的支持是衆多強力特性之一,但大多數情況下跟蹤併發程序還是很睏難,併且在線性程序中我們的直覺往往還會讓我們誤入歧途。如果這是你第一次接觸併發,那麽我推薦你稍微多花一些時間來思考這兩個章節中的樣例。
|
盡管Go對併發的支持是衆多強力特性之一,但跟蹤調試併發程序還是很睏難,在線性程序中形成的直覺往往還會使我們誤入歧途。如果這是讀者第一次接觸併發,推薦稍微多花一些時間來思考這兩個章節中的樣例。
|
||||||
|
Loading…
Reference in New Issue
Block a user