這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
訊號
sigRecv1:=make(chan os.Signal,1)sigs1:=[]os.Signal{syscall.SIGINT,syscall.SIGQUIT}signal.Notify(sigRecv1,sigs1...)sigRecv2:=make(chan os.Signal,1)sigs2:=[]os.Signal{syscall.SIGINT,syscall.SIGQUIT}signal.Notify(sigRecv2,sigs2...)// 接著我們用兩個for迴圈來接收訊息.考慮到主程式會退出,所以用waitgroup等待完成後退出.var wg sync.WaitGroupwg.Add(2)go func(){ for sig:=range sigRecv1{ fmt.Printf("Received a signal from sigRecv1%v",sig) } fmt.Printf("End. [sigRecv1]\n")}()go func(){ for sig:=range sigRecv2{ fmt.Printf("Received a signal from sigRecv1%v",sig) } fmt.Printf("End. [sigRecv2]\n")}()wg.Wait()
int socket(int domain,int type,int protocol)
socket type 類型
TCP UDP
tcp 連結類型
可能會傳回io.EOF,表明串連結束
var dataBuffer bytes.Bufferb:=make([byte,10])for { n,err:=conn.Read(b) if err!=nil{ if err==io.EOF{ fmt.Println("The connection is closed.") conn.Close() } else{ fmt.Printf("Read Error:%s\n",err) } break } dataBuffer.Write(b[:n])}
bufio 是 Buffered I/O縮寫.
由於net.Conn類型實現了介面類型io.Reader中的Read介面,所以該介面的類型的一個實作類別型。因此,我們可以使用bufio.NewReader函數來封裝變數conn,像這樣:
reader:=bufio.NewReader(conn)
可以調用reader變數的ReadBytes(“\n”)來接受一個byte類型的參數.
當然很多情況下並不是尋找一個但直接字元那麼簡單。比如,http協議中規定,訊息頭部的資訊的末尾是兩個空行,即是字串”\r\n\r\n”,
writer:=bufio.NewWriter(conn)來寫入資料
### close()
關閉串連
### LocalAddr & RemoteAddr 方法
conn.RmeoteAddr().Network() .String()
### SetDeadline, SetReadDeadline,SetWrteDeadline
b:=make([]byte,10)
conn.SetDeadline(time.Now().Add(2*time.Second))
for {
n,err:=conn.Read(b)
}
我們通過調用time.Now()方法當前絕對時間加2s.2s後的那一刻.假設之後的第二次迭代時間超過2s.則第三次迭代尚未開始,就已經逾時了,所以改為.
“`
b:=make([]byte,10)
for {
conn.SetDeadline(time.Now().Add(2*time.Second))
n,err:=conn.Read(b)
}
“`
### 一個server 的簡單實現
緩衝器中的緩衝機制,在很多時候,它會讀取比足夠多更多一點的資料到其中的緩衝器。會產生提前讀取的問題.
net/http在 net/tcp的基礎上構建了非常好用的介面,除此以外,標準庫,net/rcp中api為我們提供了兩個go程式之間建立通訊和交換資料的另外一種方式。
遠端程序呼叫(remote procedure call)也是基於TCP/IP協議棧的。
Unix系統中,POSIX標準中定義的線程及其屬性和操作方法被廣泛認可.
Linux中稱為NPTL。
## 多線程
線程一個id, TID.
編寫高並發程式的建議:
1. 控制臨界區的純度。臨界區中的僅僅包含操作共用資料的代碼。
1. 控制臨界區的粒度。
1. 減少臨界區中代碼的執行耗時。
1. 避免長時間的持有互斥變數。
1. 優先使用院子操作而不是互斥量。
GO語言是作業系統提供的核心線程之上搭建了一個特有的兩級執行緒模式。
Goroutine代表的正確的含義:
不要用共用記憶體的方式來通訊。作為替代,應該用通訊作為手段來共用記憶體。
把資料放在共用記憶體地區中供多個線程中的程式訪問的這種方式雖然在基本思想上非常簡單,但是卻讓並發存取控制變得異常複雜。只有做好了很多約束和限制,才有可能讓這些簡單的方法正確的實施。但是正確性的同時,也需要有延展性。
Go語言不推薦用共用記憶體區的方式傳遞資料。作為替代,優先使用Channel。作為多個Goroutine之間的傳遞資料,並且還會保證其過程的同步。
GO語言線程3個必須知道的核心元素。
M:machine.一個M代表了一個核心線程。
P:processor.一個P代表了M所需的上下文Content.
G:Goroutine.一個G代表了對一段需要被並發執行的GO語言代碼的封裝。
GO並發編程.
type IntChan chan int.
元素類型為int 的通道類型。
這樣的通道是雙向類型的。
如果向一個nil(被關閉的channel)發送訊息,則這個進程會被永久阻塞。
需要注意的是:當我們向一個通道發送訊息的時候,我們得到的是一個值的copy,當這個copy形成之後,之後對原來的值的修改都不會影響通道中的值。
select例子:
select語句和switch語句不同的是,跟在每個case 後面的只能是針對某個通道的發送語句或者接受語句。
針對每個case 都有初始化了一個通道。通道的類型不受任何約束。元素的類型和容量是任意的。
分支選擇規則
重做到右,從上到下。
但是當系統發現第一個滿足選擇條件的case時,運行時系統就會執行該case所包含的語句。這也意味著其他case 會被忽略。如果同時有多個case滿足條件,那麼運行時,系統會通過一個偽隨機演算法決定那個case會被執行。例如下面的代碼,發送5個範圍在【1,3】的整數:
package mainimport ( "fmt")func main() { chanCap := 5 ch7 := make(chan int, chanCap) for i := 0; i < chanCap; i++ { select { case ch7 <- 1: case ch7 <- 2: case ch7 <- 3: } } for i := 0; i < chanCap; i++ { fmt.Printf("%v\n", <-ch7) }}
但是,如果所有的case 都不滿足(並且沒有default case),就會阻塞住。直到某個case 被觸發。
所以通常情況下,default case 都是必要的.且default只能包含一個default case.
兩個變數賦值, e,ok:=<-chan
第二個變數指的是通道是否被關閉。
package mainimport ( "fmt")func main() { go func(){ for { select{ case e,ok:=<-ch11: if !ok{ fmt.Println("End.") break }else{ fmt.Printf("%d\n",e)ß } } } }}//修改方案如下,不然會死結。func main(){ go func(){ var e int ok:=true //聲明在外,方便外層使用 for { select{ case e,ok=<-ch11: if !ok{ fmt.Println("End.") break }else{ fmt.Printf("%d\n",e) } } if !ok{ break } } }}
有的時候我們需要早點關閉流程。這裡我們添加新的逾時行為。
timeout:=make(chan bool,1)go func(){ time.Sleep(time.Millisecond) timeout<-false}
在原來的基礎上,添加
go func(){ var e int ok := true for { select{ case e,ok=<-ch11: ... case ok=<-timeout: fmt.Println("Timeout.") break }} if !ok{ break }}
非緩衝的Channel
緩衝通道:由於元素的傳遞是非同步,所以發送操作在成功向通道發送元素值之後就會立即結束。
非緩衝通道:等待能夠接受該元素值的那個接收操作。並且只有確保成功接收,才會真正的完成執行。
package mainimport ( "fmt" "time")func main() { unbufChan := make(chan int) go func() { fmt.Printf("Sleep a second ...\n") time.Sleep(time.Second) num := <-unbufChan fmt.Printf("Received a integer %d.\n", num) }() num := 1 fmt.Printf("Send integer %d ...\n", num) unbufChan <- num fmt.Printf("Done")}
select 語句與非緩衝通道
在使用select語句向某個非緩衝通道發送元素的時候,我們需要打起雞血。因為,與操作緩衝通道的select語句相比,它被阻塞的機率非常之大。其基本原因依然是非緩衝通道會以同步的方式傳遞元素值。
time包與Channel
定時器
首先,結構體類型,time.Timer. new的兩種方法:
time.NewTimer(Duration) & time.AfterFunc.Duration=3*time.Hour+36*.time.MinuteTimer{ Reset() Stop()}//到達時間,是通過欄位C(chan time.Time)緩衝通道。 時間一道向自己發送一個time.Time類型元素。絕對到期時間。
重構之前的結構:
case <-time.NewTimer(time.Millisecond).C: fmt.Println("Timeout") ok=false break
定時器是可以被複用的。所以在case中的接受雨具中初始化是浪費的。下面的使用方式更好:
go func(){ var timer *time.Timer for{ select{ case <-func()<-chan time.Time{ if timer==nil{ timer=time.NewTimer(time.Millisecond) }else{ timer.Reset(time.Millisedcond) } return timer.C }(): fmt.Println("Timeout") ok=false break } }}
斷續器
斷續器與定時器的使用情境不同,當作逾時觸發器是不合適的。因為它對到期事件的傳達雖然可能被放棄,當絕對不會被延誤。斷續器一旦被初始化,它所有的到期時間都是固定的了。
固定不變的到期時間恰恰使斷續器非常適合被作為定時任務的觸發器。
例如要求兩次執行之間的最短間隔時間為10分鐘。我們可以這樣編寫滿足這一需求的代碼:
高並發負載平衡器
QPS(Query Per Second,每秒查詢量) & TPS(Transactions Per Second,每秒事物處理量)。
切片和數組都不是並發安全的。所以用一個chan來儲存結果。