P88 今日内容
go的優勢就在于并發
P89内容回顧
go語言的日期要記住,2006-01-02 13:04:05
還有after和before
runtime.caller可以擷取到運作時的堆棧資訊
接口類型的變量底層分為兩部分:動态類和動态值。
反射的應用,可以解析json,xml各種資料解析,還有orm架構。
反射的兩個方法:reflact.Typeof得到動态類型的資訊,reflact.valueof動态值類型
把字元串解析到student的結構體
unmarshall參數是空接口,可以接收任何類型的值,然後反射去把對應字段解析
P90 ini配置檔案解析1
可以試試map類型,讀取特定字元串,值存入到相應的字段裡
讀檔案,讀出位元組類型的資料
ptr就是指針
傳回的是一個error類型
格式化輸出後傳回一個error類型
t是指針類型,elem拿到值,等于先判斷是不是指針,再判斷是不是結構體
make初始化隻針對slice,map,channel,剩下都是new,new是給值類型做初始化的。必須是要一個結構體指針,傳一個int類型的指針是不行的
換成結構體指針就沒問題了
讀檔案,ioutil,資料儲存到一個變量,然後按照換行符切割。
P91 init配置解析2
配置檔案讀取出來放到了一個slice裡
一行行讀資料,遇到注釋的就跳過。如果[開頭就是段落section,把【】去掉,讀取下面的内容
提示這裡有文法錯誤
現在格式沒問題,就沒有文法錯誤了
擷取字段名,并在相應data裡根據反射找對應的結構體,造一個結構體的嵌套,找到mysql對應的結構體,再從對應結構體裡找不同的字段
結構體名字接收變量
for循環周遊拿到資料裡的值,找到對應的mysql段落還是其他段落
使用指針
如果走到了【】段落名,那就跳出循環,讀取該段落的配置字段了,
1.也就是以等号分割這一行,等号左邊是key,右邊是value。(不規範的要去掉,提示文法錯誤)
2.根據structname去data裡面把對應的嵌套結構體給取出來。
3.周遊嵌套結構體的每一個字段,判斷tag是不是等于key。
4.如果是key=tag,給這個字段 指派
空行跳過
這樣就沒問題了
根據struct name去data裡把對應的字段值取出來
先把key拿出來,切到等于号,key就是address,value就是後面的ip位址。
周遊嵌套結構體字段,判斷tag是不是等于key
下面拿到的值也需要稍微改動,等于key就找到了對應的字段
第四步,指派
把值存起來
找到對應字段就退出
下面判斷obj,reflact如果是一個string,就設定成string
這裡字元串就都有了
字元串有了,加個int類型,那就涉及到把之前string類型的數字轉成int類型
可以使用strconv.parseint,轉幾進制數,多少位,傳回int64和err
10進制數,64位,現在parseint還有問題
解析錯誤,17行有問題
xxx這裡應該找不到字段,就直接跳過了,沒有必要指派
port的值類型錯誤。
現在就都加載出來了
判斷float
添加一個test布爾值
流程:從配置檔案讀取出,把資料依次指派到結構體裡,利用反射一個字段一個字段去找,根據結構體tag值找到key值,
所有配置檔案解析,go語言裡都是做map的
也有現成的ini庫
P92 strconv标準庫介紹
go是強類型語言,類型和類型之間不能互相轉換,支援強制轉換的前提是支援轉換的,int64轉int32,數字和字元串類型就不能互相轉了。
數字當作ascii碼了,拿編碼找到了這個字元
拿到字元串的97,直接string(),是用數字做一個utf-8編碼了,去找對應符号去了
還有一個方法,strconv.parseint,字元串轉int
這樣1000這個字元串就轉成int64了,前面的10代表10進制
10,0,傳0的話就是int類型,但傳回值定死了,就是int64
這樣就傳回int
但是轉成int一般不用上面的,Atoi就是字元串轉成int
數字轉換成字元串Itoa
strconv是把對應的字元傳解析成需要用的資料,比如boolstr
P93 并發程式設計介紹
java做并發,需要os裡線程池,包裝一下,起一個線程,執行完再關閉,這裡os線程運作的時候會遇到上下文切換,一個線程最起碼要2M。
go裡實作了自己的線程,goroutine,切換的時候是自己切。
并發代表同一段時間 内執行多個任務。
并行代表同一時刻執行多個任務。
go的并發用goroutine實作。等于在cpu阻塞的時候去做其他的事情。類似于線程,但是是屬于使用者态的線程。
goroutine是由go語言的運作時runtime排程完成的。
channel是在用多個goroutine進行通信的,協調工作用的。
P94 建立goroutine
在調用函數的時候前面加上go 關鍵字,就能實作并發。
**
程式啟動之後會建立一個主goroutine去執行,go關鍵字代表開啟一個單獨的goroutine去執行hello函數(任務)**
現在列印一個main,main函數結束,由main函數啟動的goroutine也都結束了
會停頓一下
列印多個,列印的時候順序不一定
P95 sync.WaitGroup
改造成匿名函數,為什麼有3個999,列印i這個變量,這個變量是在外面拿的,啟動一個go routine的 時候,可能for循環轉的夠快,已經走了好幾個循環了,是以列印的時候出現了多個。
可以在函數執行的時候把參數傳進去,這樣每個for循環就把值主動 傳進去了。說明啟動goroutine是要消耗時間和資源的。
**優雅方式等待goroutine去結束,sync包裡有一個Waitgroup
**
goroutine對應的函數執行結束了,gocroutine才結束
程式編譯好了,每次執行的都一樣,指派進去了,每次執行都是一樣的
1970年到現在的納秒數,這樣肯定是不一樣的,這樣 寫的程式的随機數就沒問題
struct是值類型
done代表計數器結束了
通常這麼寫,確定done了
啟動10個goroutine
等待計數器減為0就退出,3可能還沒來得及輸出就退出了
等待時間 長點就可以看到3了
sync.waitgroup可以同步多個goroutine退出才退出
P96 goroutine排程模型GMP
goroutine是使用者态的線程 ,是go語言自己實作的類似線程的東西。
goroutine和線程 的差別就是棧不一樣 。os線程一般都固定棧的記憶體是2M,一個goroutine的棧不是固定的,最小2Kb,最大1GB。
是以go語言中可以一次建立十萬作用的goroutine也是可以的。
goroutine排程,GMP排程
G:就是goroutine,裡面除了存放本goroutine資訊外還有與所在ip的綁定等資訊。
M:machine 是Go運作時對作業系統核心線程的虛拟,M與核心線程一般是映射關系,一個gorutine最終是要放到M上執行的。
P:管理一組goroutine隊列,P裡面會存儲目前goroutine運作的上下文環境(函數指針,堆棧位址及位址邊界),P會對自己管理的goroutine隊列做一些排程(比如把占用cpu時間較長的goroutine暫停、運作後續的goroutine等等)當自己的隊列消費完了就去全局隊列裡重取,如果全局隊列裡也消費完了會去其他P的隊列裡搶任務。
當io阻塞的時候 ,就會切換。p的個數是通過runtime.GOMAXPROCS設定,最大256,go1.5版本之後預設為實體線程數,在并發量打的時候會則更加一些P和M,但不會太多。
所有都是p排程的,也就是go語言自己排程,比其他語言用作業系統排程,就省很多資源。把m個goroutine放到一個作業系統線程上進行工作。
windows無效,體會不出來,先試個1核
先列印B再列印A
多核可能會出現列印錯亂的情況
預設就是cpu的核心數
作業系統啟動的是程序,但是真正幹活的是線程
作業系統線程核goroutine的關系:
1個作業系統線程對應使用者多個goroutine
go程式同時可以有多個作業系統程式設計。
goroutine核OS線程是多對多關系。
goroutine排程模型,GMP
G是goroutine
M是用于和作業系統線程做映射的。
P是管理goroutine,阻塞的時候進行切換
P97 channel初識
單純讓函數起一個goroutine沒有意義,肯定是中間有多個環節進行互動。
現在不是使用共享記憶體來實作資料通信(這樣會資料加鎖,并行變串行)而是通過通信,實作共享記憶體。go語言用通道實作多個goroutine通信的。
goroutine是go語言并發的執行體,channel相當于做連結。channel是一個特殊類型,類似傳送帶,遵循先入先出
需要指定通道中元素的類型,channel是一個引用類型,需要初始化去使用,配置設定記憶體使用。
make,初始化slice,map,channel
帶緩沖區和不帶緩沖區,滿了的話有人接才能放
channel的使用有三種操作,發送,接收,關閉
不管發送還是接收,都是用<小于号
發送是從goroutine到channel,接收是從channel到goroutine
相當于卡在這裡了
等于永遠放不進去,現在是一個main的一個goroutine
可以再起一個匿名函數試試
使用sync.waitgroup等待goroutine結束
現在就列印出來了
下面的放進去,上面的才能取出來
沒有緩沖區的channel
帶緩沖區的channel,初始化裡,裡面最多存一個容量,是以下面就可以放進去
取值
隻有1個容量,是以,下面的就卡住了
關閉close掉
箭頭代表資料的流向
如何優雅地從通道循環取值
forange也支援從通道取值
P98 channel練習
先造100個數放到通道1,另外的goroutine從通道1取值,每個乘平方,然後把結果放到通道2裡。
f1循環放值,f2循環取值
最後讀不到資料,會傳回一個false,隻要不關閉,一直都是true
這個數就算出來了
sys.once隻執行一次
once確定某個操作隻執行一次,隻關閉一次
關閉通道的時候,如果原來有值,讀會繼續讀,之後傳回值是false
對一個關閉通道繼續取值,是可以取的,隻不過會是false
關閉的通道取多少次都是false,bool是false,字元串就是為空
P99 單向通道
單向通道一般用在函數的參數裡,限制别人隻想發送什麼,或接收什麼。確定暴露出去的通道隻能有一項操作。限制你一個函數裡隻能往ch2裡放進去,不能取出來。
傳回的是一個通道,通道類型是時間類型。單向通道,隻能從裡面去讀取。
channel三種操作,接收發送,關閉。
通道狀态,nil,非空,空的,滿了,沒滿。
nil沒有初始化的時候,接收,發送都是阻塞的,關閉會panic,關閉已經關閉的會panic。
如果主程式一直運作,後面goroutine一直占記憶體,永遠不可能把記憶體釋放,這是goroutine洩露
P100 work_pool練習
goroutine池,work_pool這種模式
造三個goroutine去執行任務
worker是包裝了一個任務,id是int,jobs是一個隻讀的通道,result是隻寫的通道。
從隻讀通道取值,算乘2 的值
起三個goroutine,5個任務
id為3的goroutine先起5個任務
開啟一個goroutine循環加入job
從job取數,計算合,發送到result
列印數
P101 select介紹
從多個通道去取,叫多路複用。
想要實作每次随機從c1或者c2裡取
c1取不到到c2裡取,c2取不到到c1裡取
select,多個case滿足,可以随機選一個
這樣每次執行結果都不一樣
P102 作業要求
之前的日志是同步寫的,可以起一個goroutine,日志放到channel裡,一直從channel裡拿日志寫檔案。