網站首頁 編程語言 正文
一、Go語言中Goroutine的基本原理
Go語言里的并發指的是能讓某個函數獨立于其他函數運行的能力。
Go語言的goroutine是一個獨立的工作單元,
Go 語言的并發同步模型來自一個叫作通信順序進程(Communicating Sequential Processes,CSP)的范型(paradigm)。
CSP 是一種消息傳遞模型,通過在goroutine 之間傳遞數據來傳遞消息,而不是對數據進行加鎖來實現同步訪問。消息的傳遞通過Go語言中的Channel(通道)來實現。
進程(process) 看作一個包含了應用程序在運行中需要用到和維護的各種資源的容器。
線程(Thread)是一個執行空間,這個空間會被操作系統調度來運行函數中所寫的代碼。
每個進程至少包含一個線程,每個進程的初始線程被稱作主線程。因為執行這個線程的空間是應用程序的本身的空間,所以當主線程終止時,應用程序也會終止。
下圖就是進程和線程的簡要關系。
補充:句柄是什么?
我的理解句柄就是給用戶操作內核資源的指針,指向指針的指針?!景咽帧亢汀鹃T】的關系,使用們把手能轉動整個門(系統資源)
操作系統會在物理處理器上調度線程來運行,而Go 語言的運行時會在邏輯處理器上調度goroutine來運行。每個邏輯處理器都分別綁定到單個操作系統線程。下面我們就來簡單描述一下goroutine執行的流程。
首先來了解一下操作系統線程、邏輯處理器和本地運行隊列
全局運行隊列:剛創建的goruntine會被安排到這里面,通過一些調度算法,分配給邏輯處理器
操作系統線程:這個就是傳統意義上的線程,用于具體goroutine的執行,他會和一個邏輯器進行綁定
邏輯處理器:里面維護著一個本地的隊列,用于本地隊列里面goroutine的調度
本地運行隊列:裝載著待執行的goruntine
支撐整個調度器的主要有4個重要結構,分別是M、G、P、Sched,前三個定義在runtime.h中,Sched定義在proc.c中。
- Sched結構就是調度器,它維護有存儲M和G的隊列以及調度器的一些狀態信息等。
- M代表內核級線程,一個M就是一個線程,goroutine就是跑在M之上的;M是一個很大的結構,里面維護小對象內存cache(mcache)、當前執行的goroutine、隨機數發生器等等非常多的信息。
- P全稱是Processor,處理器,它的主要用途就是用來執行goroutine的,所以它也維護了一個goroutine隊列,里面存儲了所有需要它來執行的goroutine,這個P的角色可能有一點讓人迷惑,一開始容易和M沖突,后面重點聊一下它們的關系。
- G就是goroutine實現的核心結構了,G維護了goroutine需要的棧、程序計數器以及它所在的M等信息。
整個過程描述:
當創建一個Goroutine的時候,先放到全局隊列當中,然后會把這個Goroutine分配到一個邏輯處理器的本地隊列中,這個邏輯處理器會綁定一個操作系統線程,由這個線程去執行Goroutine的代碼。
生動描述:
地鼠(gopher)用小車運著一堆待加工的磚。M就可以看作圖中的地鼠,P就是小車,G就是小車里裝的磚。一圖勝千言啊,弄清楚了它們三者的關系,下面我們就開始重點聊地鼠是如何在搬運磚塊的。
1.runqget, 地鼠(M)試圖從自己的小車(P)取出一塊磚(G),當然結果可能失敗,也就是這個地鼠的小車已經空了,沒有磚了。
2.findrunnable, 如果地鼠自己的小車中沒有磚,那也不能閑著不干活是吧,所以地鼠就會試圖跑去工場倉庫取一塊磚來處理;工場倉庫也可能沒磚啊,出現這種情況的時候,這個地鼠也沒有偷懶停下干活,而是悄悄跑出去,隨機盯上一個小伙伴(地鼠),然后從它的車里試圖偷一半磚到自己車里。如果多次嘗試偷磚都失敗了,那說明實在沒有磚可搬了,這個時候地鼠就會把小車還回停車場,然后睡覺休息了。如果地鼠睡覺了,下面的過程當然都停止了,地鼠睡覺也就是線程sleep了。
3.wakep, 到這個過程的時候,可憐的地鼠發現自己小車里有好多磚啊,自己根本處理不過來;再回頭一看停車場居然有閑置的小車,立馬跑到宿舍一看,你妹,居然還有小伙伴在睡覺,直接給屁股一腳,“你妹,居然還在睡覺,老子都快累死了,趕緊起來干活,分擔點工作。”,小伙伴醒了,拿上自己的小車,乖乖干活去了。有時候,可憐的地鼠跑到宿舍卻發現沒有在睡覺的小伙伴,于是會很失望,最后只好向工場老板說——”停車場還有閑置的車啊,我快干不動了,趕緊從別的工場借個地鼠來幫忙吧?!保詈蠊隼习寰透銇硪粋€新的地鼠干活了。
4.execute,地鼠拿著磚放入火種歡快的燒練起來。
注: “地鼠偷磚”叫work stealing,一種調度算法。
到這里,貌似整個工場都正常的運轉起來了,無懈可擊的樣子。不對,還有一個疑點沒解決啊,假設地鼠的車里有很多磚,它把一塊磚放入火爐中后,何時把它取出來,放入第二塊磚呢?難道要一直把第一塊磚燒練好,才取出來嗎?那估計后面的磚真的是等得花兒都要謝了。這里就是要真正解決goroutine的調度,上下文切換問題。
goroutine的阻塞
當一個OS線程M0陷入阻塞時(如下圖),P轉而在運行M1,圖中的M1可能是正被創建,或者從線程緩存中取出。
簡單解釋就是,當當前的Goroutine在阻塞,就把他和當前線程綁定,讓當前的線程繼續執行這個Goroutine(就是等待),其他的goroutine隨著邏輯處理器被綁定到另一個線程上,繼續執行。
當阻塞的線程返回時,它必須嘗試取得一個邏輯處理器來運行goroutine,一般情況下,它會從其他的OS線程那里拿一個P過來,如果沒有拿到的話,它就把goroutine放在一個全局運行隊列里,然后自己睡眠(放入線程緩存里)。所有的P也會周期性的檢查global runqueue并運行其中的goroutine,否則global runqueue上的goroutine永遠無法執行。
還有就像下面的圖片,左邊的線程一個滿載狀態,一個沒有goroutine,此時的做法就是會進行重新分配,就想右側圖展示
參考 :https://morsmachine.dk/go-scheduler
二、Go語言中的并發和并行
并發是兩個任務可以在重疊的時間段內啟動,運行和完成。并行是任務在同一時間運行,例如,在多核處理器上。
并發是獨立執行過程的組合,而并行是同時執行(可能相關的)計算。
并發是一次處理很多事情,并行是同時做很多事情。
- 應用程序可以是并發的,但不是并行的,這意味著它可以同時處理多個任務,但是沒有兩個任務在同一時刻執行。
- 應用程序可以是并行的,但不是并發的,這意味著它同時處理多核CPU中的任務的多個子任務。
- 應用程序可以即不是并行的,也不是并發的,這意味著它一次一個地處理所有任務。
- 應用程序可以即是并行的也是并發的,這意味著它同時在多核CPU中同時處理多個任務。
簡單講:并行就是同時做很多事 并發就是一堆事情一個時間點來了,然后通過分片等方式感覺像都在執行
原文鏈接:https://www.cnblogs.com/dcz2015/p/10106866.html
相關推薦
- 2022-08-19 python查看自己安裝的所有庫并導出的命令_python
- 2023-10-26 無法加載文件 C:\Users\sundear\AppData\Roaming\npm\dva.ps
- 2024-03-23 springboot項目中如何獲取請求頭當中的token
- 2023-01-28 C#實現XML文件操作詳解_C#教程
- 2022-04-05 Pandas的DataFrame如何做交集,并集,差集與對稱差集_python
- 2022-06-26 R語言實現PCA主成分分析圖的示例代碼_R語言
- 2023-04-18 C#?TabControl手動觸發DrawItem的實現_C#教程
- 2022-08-02 深入了解Golang的map增量擴容_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支