什麼是CSP併發模型:
嚴格的說CSP 應該是一門形式語言: 用於描述併發系統的互動模式。
CSP 中文名稱叫 通信順序進程, 是一個 很強大的併發編程模型,是Go語言成功的中重要因素 : go語言並未完全使用CSP模型, 而僅僅使用了 process 和 channel兩個概念
process : 在go語言中 即 goroutine
go語言使程序員開發時 更輕鬆, 而不用考慮 線程數 ,線程 開銷,線程調度 等底層問題,goroutine天生就爲我們實現好了。
goroutine 和channel是 go語言 併發編程的兩大基石: goroutine 負責執行併發業務, 而channel 用於goroutine之間的同步通信。 goroutine 是 線程安全的
go的併發哲學
不要使用共享內存 來通信, 而要通過通信來實現共享內存。
意味着我們不要 藉助 sync包實現併發編程,而強烈建議通過channel實現併發編程。
四 Goroutine:
Goroutine是實際併發執行的實體:底層通過 coroutine(協程)來實現併發, 協程是一種運行在用戶態的線程, go底層使用協程的出發點是因爲:協程具有以下特點:
1 使用的是用戶空間,避免了 用戶態和內核態切換的開銷
2 可以由語言或框架進行調度
3 協程具有更小棧空間, 那麼允許創建大量的協程實例
反射初識
反射就是在程序運行是時,能夠觀察自己, 並修改自己的行爲的一種機制。
反射是和接口類型相關的 , 只有interface 類型纔有反射這一說
要理解反射 ,就得理解 接口的pair 屬性 。
pair(實體值, 類型)
反射 實質: 在運行時,能夠動態的獲取 接口的 值 和類型, 進而進行一些其他的操作。
reflect包 :下 TypeOf() 和 ValueOf ()
1 現有 接口類型變量
2 轉化爲 reflect 類型
3 通過接口來獲取
package main
import (
"fmt"
"reflect"
)
func main(){
var a float64 = 4.13 // int64 類型 可以看作是一個 空接口類型, 所以可以直接使用
fmt.Println("type:",reflect.TypeOf(a))
}
需要反射的兩個場景:
1 有時候編寫的函數, 不知道需要接受什麼類型,
2 有時候需要根據某些條件來決定調用哪個函數,比如由用戶的輸入來確定, 這時候就需要對函數和函數的參數進行反射,在運行期間動態的執行函數。
package main
import (
"fmt"
"reflect"
)
func main(){
var num float64 =1.23
// 由接口類型變量 -->得到反射類型對象
value := reflect.ValueOf(num)
fmt.Println("由接口類型變量 --> 得到反射類型對象 : ",value)
convertValue := value.Interface().(float64)
fmt.Println("由反射類型對象 --> 轉化得到接口類型變量:",convertValue)
fmt.Printf("%T\n",convertValue)
}
注意下邊代碼只是用來檢驗 反射 ,並不是說這樣寫更科學
package main
import (
"fmt"
"reflect"
)
type Person struct{
Name string
Age int
Sex string
}
func (p Person)printInfo(){
fmt.Println(p.Name, " ", p.Age," ", p.Sex)
}
func (p Person)readInfo(){
fmt.Println("Name :",p.Name, "Age :", p.Age,"Sex :", p.Sex)
}
func testReflect(p interface{}){
fmt.Println("P is :", p)// 檢驗普通做法
//檢驗 反射 做法
getValue := reflect.ValueOf(p)
getType := reflect.TypeOf(p)
fmt.Println("reflect value is : ", getValue)
fmt.Println("reflect type is : ", getType)
i := getValue.Kind()
fmt.Println("value kind is :", i)
/*
獲取 p的字段
*/
for i:=0; i < getType.NumField(); i++{
field := getType.Field(i)
fmt.Println("字段名稱:",field.Name,"字段類型:", field.Type,
"字段數值:",getValue.Field(i).Interface())
}
}
func main(){
/*
var num float64 =1.23
// 由接口類型變量 -->得到反射類型對象
value := reflect.ValueOf(num)
fmt.Println("由接口類型變量 --> 得到反射類型對象 : ",value)
convertValue := value.Interface().(float64)
fmt.Println("由反射類型對象 --> 轉化得到接口類型變量:",convertValue)
fmt.Printf("%T\n",convertValue)
*/
p := Person{"小二",19,"男"}
testReflect(p)
}
反射對象.Kind() 是說種類 :eg: slice 、 map、 pointer、 struct、interface、 array\ Function 、int等等
Type()是說類型: 比如 : Person 類 等
比如
type Person struct {
…
}
那麼 Kind 就是 struct 而 Type 是 Person
如何通過反射對象 設置 實際變量的值?
** 注意的是: 需要修改 必須拿到指針。纔可以修改**
設置實際變量的值 : 指針方式
package main
import (
"fmt"
"reflect"
)
func main(){
var num float64 =1.23
fmt.Println("num->",num)
pointer:= reflect.ValueOf(&num)
newValue := pointer.Elem()//獲取指針指向的反射對象(原始對象)
fmt.Println("newValue -> ",newValue)
fmt.Println("newValue類型-->",newValue.Type())
fmt.Println("newValue種類-->",newValue.Kind())
fmt.Println("newValue是否可以修改對象:",newValue.CanSet())
newValue.SetFloat(3.14)
fmt.Println("newValue is ->",newValue)
fmt.Println("num is->",num)
}
注意:
1 要通過 反射來調用對應的方法,必須先通過reflect.ValueOf()獲取到 “反射類型的對象“後才能做下一步處理,
2 反射類型對象.MethodByName() 這個 MethodByName,需要指定準確的真實的方法名字, 錯了就會直接panic, MethodByName 返回一個函數值對應的reflect.Value(反射類型對象)方法的名字
3 【】reflect.Value, 是最重要調用的方法的參數, 我們用切片進行存儲, 空的時候也可以傳nil
4 反射類型對象的Call 方法,這個方法最終調用真實的方法,參數必須保持一致, 如果反射類型對象.Kind不是一個方法, 那麼直接panic
5 ( 本來可以用對象直接調用方法的) 我們採用反射,首先得將方法註冊,也就是MethodByName, 然後通過反射調用methodValue.Call