golang入門day7(CSP 併發模型 + 反射)

什麼是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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章