golang解析---進程,線程,協程

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"一.背景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在併發編程中進程和線程是不可忽略的兩個概念,他們很好的完成了操作系統或者服務對於高併發的需求,然而隨着時代的進步,協程的概念應運而生,本文旨在解釋協程相對於進程和線程在高併發環境下的優勢,所以會先介紹進程,線程,最後講解協程的調度方式。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"二.詳細介紹","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.1進程","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1.1概念","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程基本上是一個正在執行的程序,它是操作系統中最小的資源分配單位。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1.2結構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當一個程序被加載到內存中併成爲一個進程時,它可以分爲四個部分——堆棧、堆、文本和數據。下圖顯示了主內存中進程的簡化佈局:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0b/0b0056a241c559c2612565c00c827980.png","alt":"過程對比 線","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"堆棧","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程堆棧包含臨時數據,例如方法/函數參數、返回地址和局部變量。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"堆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是在進程運行時動態分配給進程的內存。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包含全局變量和靜態變量。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"文本","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包括由程序計數器的值和處理器寄存器的內容表示的當前活動。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1.3進程上下文切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程的上下文切換是指cpu從一個進程切換到另一個進程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程上下文切換主要包含兩個主要過程:進程地址空間切換和處理器狀態切換","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"進程地址空間切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"切換原因","attrs":{}},{"type":"text","text":":進程地址空間指的是進程所擁有的虛擬地址空間,而這個地址空間是假的,是linux內核通過數據結構來描述出來的,從而使得每一個進程都感覺到自己擁有整個內存的假象,cpu訪問的指令和數據最終會落實到實際的物理地址,對用進程而言通過缺頁異常來分配和建立頁表映射。進程地址空間內有進程運行的指令和數據,因此到調度器從其他進程重新切換到我的時候,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"爲了保證當前進程訪問的虛擬地址是自己的必須切換地址空間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"切換方式","attrs":{}},{"type":"text","text":":將當前進程的pgd虛擬地址轉換爲物理地址存放在用戶控件的頁表基址寄存器,當訪問用戶空間地址的時候mmu會通過這個寄存器做遍歷頁表,獲得物理地址。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原理是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"進程想要訪問一個用戶空間虛擬地址,cpu的mmu所做的工作,就是從頁表基址寄存器拿到頁全局目錄的物理基地址,然後和虛擬地址配合來查查找頁表,最終找到物理地址進行訪問(當然如果tlb命中就不需要遍歷頁表),每次用戶虛擬地址訪問的時候(內核空間共享不考慮),由於頁表基地址寄存器內存放的是當前執行進程的頁全局目錄的物理地址,所以訪問自己的一套頁表,拿到的是屬於自己的物理地址(實際上,進程是訪問虛擬地址空間的指令數據的時候不斷髮生缺頁異常,然後缺頁異常處理程序爲進程分配實際的物理頁,然後將頁幀號和頁表屬性填入自己的頁表條目中),就不會訪問其他進程的指令和數據,這也是爲何多個進程可以訪問相同的虛擬地址而不會出現差錯的原因","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ps:地址空間切換過程中,還會清空tlb(頁表緩存:用於存放虛擬地址映射至物理地址的標籤頁表條目)","attrs":{}},{"type":"text","text":",防止當前進程虛擬地址轉化過程中命中上一個進程的tlb表項,一般會將所有的tlb無效,但是這會導致很大的性能損失,因爲新進程被切換進來的時候面對的是全新的空的tlb,造成很大概率的tlb miss,需要重新遍歷多級頁表","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"處理器狀態切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"切換原因","attrs":{}},{"type":"text","text":":需要將進程的內核棧和執行流進行切換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"切換方式","attrs":{}},{"type":"text","text":":處理器狀態切換就是將前一個進程的sp,pc等寄存器的值保存到一塊內存上,然後將即將執行的進程的sp,pc等寄存器的值從另一塊內存中恢復到相應寄存器中,恢復sp完成了進程內核棧的切換,恢復pc完成了指令執行流的切換。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sp寄存器在任意時刻會保存我們棧頂的地址.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"pc寄存器也稱爲程序寄存器,用於存儲指向下一條指令的地址,也即將將要執行的指令代碼。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.2線程","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2.1概念","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程是進程的子集,也稱爲輕量級進程。一個進程可以有多個線程,這些線程由調度器獨立管理。一個進程內的所有線程都是相互關聯的。線程是操作系統中最小的調度單位。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2.2結構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程有一些公共信息,例如","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"數據段、代碼段、文件等,","attrs":{}},{"type":"text","text":"這些信息共享給它們的對等線程。但包含自己的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"寄存器","attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"堆棧","attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"程序計數器","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"堆棧","attrs":{}},{"type":"text","text":":函數在被執行的時候產生的數據包括 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"函數參數","attrs":{}},{"type":"text","text":"、 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"局部變量","attrs":{}},{"type":"text","text":"、 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"返回地址","attrs":{}},{"type":"text","text":"等信息,這些信息是保存在棧中的,線程相當於進程中的一個執行流,爲了保存執行流的信息,我們需要給線程創建獨屬堆棧","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"寄存器","attrs":{}},{"type":"text","text":":函數運行需要額外的寄存器來保留一些信息,所以線程的寄存器也是私有的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"程序計數器","attrs":{}},{"type":"text","text":":CPU執行指令的信息保存在一個叫做程序計數器的寄存器中,通過這個寄存器我們就知道接下來要執行哪一條指令。所以線程也有自己的計數器用於告訴我們線程執行的工作順序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/30/304fc6c36c057baefd2b2d94bdab43d1.png","alt":"過程對比 線","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2.3線程上下文切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據線程的結構可知,線程沒有自己的地址空間,同一進程的線程之間切換,他們共享同一進程的地址空間,所以只需要切換處理器狀態;不同進程的線程之間切換,會引起進程切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於同一進程下的線程上下文切換不引起虛擬地址空間切換,所以它們上下文切換的花銷要比進程小很多。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.3協程","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3.1概念","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看作輕量級線程,他的內存佔用少只要2k,且上下文切換成本低,是一個獨立執行的函數,由go語言啓動,由 Go 運行時(runtime)管理。Go 程序會智能地將 goroutine 中的任務合理地分配給每個 CPU。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3.2結構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Go 中,goroutine 只不過是一個 Go 結構,包含有關正在運行的程序的信息,例如堆棧、程序計數器或其當前的 OS 線程。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type g struct {\n\tstack stack // offset known to runtime/cgo\n\tstackguard0 uintptr // offset known to liblink\n\tstackguard1 uintptr // offset known to liblink\n\t_panic *_panic // innermost panic - offset known to liblink\n\t_defer *_defer // innermost defer\n\tm *m // current m; offset known to arm liblink\n\tsched gobuf\n\tsyscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc\n\tsyscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc\n\tstktopsp uintptr // expected sp at top of stack, to check in traceback\n\tparam unsafe.Pointer\n\tatomicstatus uint32\n\tstackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus\n\tgoid int64\n\tschedlink guintptr\n\twaitsince int64 // approx time when the g become blocked\n\twaitreason waitReason // if status==Gwaiting\n\n\tpreempt bool // preemption signal, duplicates stackguard0 = stackpreempt\n\tpreemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule\n\tpreemptShrink bool // shrink stack at synchronous safe point\n\tasyncSafePoint bool\n\n\tpaniconfault bool // panic (instead of crash) on unexpected fault address\n\tgcscandone bool // g has scanned stack; protected by _Gscan bit in status\n\tthrowsplit bool // must not split stack\n\tactiveStackChans bool\n\tparkingOnChan uint8\n\traceignore int8 // ignore race detection events\n\tsysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine\n\ttracking bool // whether we're tracking this G for sched latency statistics\n\ttrackingSeq uint8 // used to decide whether to track this G\n\trunnableStamp int64 // timestamp of when the G last became runnable, only used when tracking\n\trunnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking\n\tsysexitticks int64 // cputicks when syscall has returned (for tracing)\n\ttraceseq uint64 // trace event sequencer\n\ttracelastp puintptr // last P emitted an event for this goroutine\n\tlockedm muintptr\n\tsig uint32\n\twritebuf []byte\n\tsigcode0 uintptr\n\tsigcode1 uintptr\n\tsigpc uintptr\n\tgopc uintptr // pc of go statement that created this goroutine\n\tancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)\n\tstartpc uintptr // pc of goroutine function\n\tracectx uintptr\n\twaiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order\n\tcgoCtxt []uintptr // cgo traceback context\n\tlabels unsafe.Pointer // profiler labels\n\ttimer *timer // cached timer for time.Sleep\n\tselectDone uint32 // are we participating in a select and did someone win the race?\n\tgcAssistBytes int64\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3.3協程上下文切換","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"goroutine調度概念介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你的go程序啓動,他會根據你的主機分配","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"邏輯處理器(P)","attrs":{}},{"type":"text","text":",每個物理核心可能有多個硬件線程,以我的電腦爲例子,顯示是6核但是可以出實話出來12個邏輯處理器,他們可用於並行執行OS線程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個邏輯處理器都會分配一個","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"OS線程(M)","attrs":{}},{"type":"text","text":",該線程由操作系統管理,當go執行,有12個線程可用於執行工作,每個線程連接到一個P。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個go程序都會有一個","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"初始的協程(G:goroutine)","attrs":{}},{"type":"text","text":",他可以被看作是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"程序級別線程","attrs":{}},{"type":"text","text":",所有的goroutine在M上進行上下文切換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後還需要有","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"運行隊列","attrs":{}},{"type":"text","text":",全局運行隊列(GRQ)和本地運行隊列(LRQ)。每個 P 都有一個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"LRQ,用於管理分配在 P 上下文中執行的 Goroutine","attrs":{}},{"type":"text","text":"。這些 Goroutine 輪流在分配給該 P 的 M 上進行上下文切換。GRQ 用於管理尚未分配給P的 Goroutine。有一個將 Goroutines 從 GRQ 移動到 LRQ 的過程,我們將在後面討論。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/11/11413e494428c2540e349637f34bbec3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程,線程的切換,都是操作系統進行調度的,go調度是go語音的一部分,它運行在內核之上的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用戶空間中","attrs":{}},{"type":"text","text":"。它不是搶佔式,而是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"協作調度","attrs":{}},{"type":"text","text":"。作爲協作調度程序意味着調度程序需要在代碼中的安全點發生的明確定義的用戶空間事件來做出調度決策。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上是goroutine的定義,從上文可知,goroutine調度與進程線程最大的區別就在於它是運行在用戶空間中的協作調度方式的上下文切換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"會觸發調度程序調度決策的場景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.go關鍵字使用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.垃圾回收","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.系統調用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.mutex,channel調用導致goroutine阻塞","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"切換開銷","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Goroutine上下文切換隻涉及修改三個寄存器(PC[程序寄存器]/SP[堆棧指針]/DX)的值,而比較線程的上下文切換需要包括模式切換(從用戶態切換到內核態)和16個寄存器,PC、SP等寄存器刷新","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"三.總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"進程上下文切換開銷:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.地址空間","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.硬件上下文","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"線程上下文切換開銷:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.硬件上下文","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.同一進程下不切換地址空間","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"goroutine切換開銷:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.用戶態,不用象線程和進程一樣多進行一次內核用戶態切換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.只需要保存/恢復三個寄存器的值,開銷遠遠小於線程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其餘優點:goroutine的棧空間爲2k,線程爲2m,進程是10m","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由進程,線程,goroutine的上下文切換可以明顯看出是一個逐步減負的過程,這個過程可以結合它們的結構來理解,coverco故而自帶goroutine的go語言在高併發開發中有着得天獨厚的優勢。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"參考文章","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.toptal.com/software/introduction-to-concurrent-programming","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.tutorialspoint.com/operating_system/os_processes.htm","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.guru99.com/process-management-pcb.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://blog.csdn.net/21cnbao/article/details/108860584","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.javatpoint.com/process-vs-thread","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://blog.csdn.net/weixin_39630048/article/details/113328415","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"協程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.youtube.com/watch?v=f6kdp27TYZs","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://talks.golang.org/2012/concurrency.slide#13(關於併發演講)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://morioh.com/p/36af32e3f52c","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章