原文:【 】
創新互聯建站成立于2013年,是專業互聯網技術服務公司,擁有項目網站制作、成都網站制作網站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元望奎做網站,已為上家服務,為望奎各地企業和個人服務,聯系電話:18980820575
如果有解答的不對的,麻煩各位在評論寫出來~
go的調度原理是基于GMP模型,G代表一個goroutine,不限制數量;M=machine,代表一個線程,最大1萬,所有G任務還是在M上執行;P=processor代表一個處理器,每一個允許的M都會綁定一個G,默認與邏輯CPU數量相等(通過runtime.GOMAXPROCS(runtime.NumCPU())設置)。
go調用過程:
可以能,也可以不能。
因為go存在不能使用==判斷類型:map、slice,如果struct包含這些類型的字段,則不能比較。
這兩種類型也不能作為map的key。
類似棧操作,后進先出。
因為go的return是一個非原子性操作,比如語句 return i ,實際上分兩步進行,即將i值存入棧中作為返回值,然后執行跳轉,而defer的執行時機正是跳轉前,所以說defer執行時還是有機會操作返回值的。
select的case的表達式必須是一個channel類型,所有case都會被求值,求值順序自上而下,從左至右。如果多個case可以完成,則會隨機執行一個case,如果有default分支,則執行default分支語句。如果連default都沒有,則select語句會一直阻塞,直到至少有一個IO操作可以進行。
break關鍵字可跳出select的執行。
goroutine管理、信息傳遞。context的意思是上下文,在線程、協程中都有這個概念,它指的是程序單元的一個運行狀態、現場、快照,包含。context在多個goroutine中是并發安全的。
應用場景:
例子參考:
waitgroup
channel
len:切片的長度,訪問時間復雜度為O(1),go的slice底層是對數組的引用。
cap:切片的容量,擴容是以這個值為標準。默認擴容是2倍,當達到1024的長度后,按1.25倍。
擴容:每次擴容slice底層都將先分配新的容量的內存空間,再將老的數組拷貝到新的內存空間,因為這個操作不是并發安全的。所以并發進行append操作,讀到內存中的老數組可能為同一個,最終導致append的數據丟失。
共享:slice的底層是對數組的引用,因此如果兩個切片引用了同一個數組片段,就會形成共享底層數組。當sliec發生內存的重新分配(如擴容)時,會對共享進行隔斷。詳細見下面例子:
make([]Type,len,cap)
map的底層是hash table(hmap類型),對key值進行了hash,并將結果的低八位用于確定key/value存在于哪個bucket(bmap類型)。再將高八位與bucket的tophash進行依次比較,確定是否存在。出現hash沖撞時,會通過bucket的overflow指向另一個bucket,形成一個單向鏈表。每個bucket存儲8個鍵值對。
如果要實現map的順序讀取,需要使用一個slice來存儲map的key并按照順序進行排序。
利用map,如果要求并發安全,就用sync.map
要注意下set中的delete函數需要使用 delete(map) 來實現,但是這個并不會釋放內存,除非value也是一個子map。當進行多次delete后,可以使用make來重建map。
使用sync.Map來管理topic,用channel來做隊列。
參考:
多路歸并法:
pre class="vditor-reset" placeholder="" contenteditable="true" spellcheck="false"p data-block="0"(1)假設有K路a href=""數據流/a,流內部是有序的,且流間同為升序或降序;
/pp data-block="0"(2)首先讀取每個流的第一個數,如果已經EOF,pass;
/pp data-block="0"(3)將有效的k(k可能小于K)個數比較,選出最小的那路mink,輸出,讀取mink的下一個;
/pp data-block="0"(4)直到所有K路都EOF。
/p/pre
假設文件又1個G,內存只有256M,無法將1個G的文件全部讀到內存進行排序。
第一步:
可以分為10段讀取,每段讀取100M的數據并排序好寫入硬盤。
假設寫入后的文件為A,B,C...10
第二步:
將A,B,C...10的第一個字符拿出來,對這10個字符進行排序,并將結果寫入硬盤,同時記錄被寫入的字符的文件指針P。
第三步:
將剛剛排序好的9個字符再加上從指針P讀取到的P+1位數據進行排序,并寫入硬盤。
重復二、三步驟。
go文件讀寫參考:
保證排序前兩個相等的數其在序列的前后位置順序和排序后它們兩個的前后位置順序相同的排序叫穩定排序。
快速排序、希爾排序、堆排序、直接選擇排序不是穩定的排序算法。
基數排序、冒泡排序、直接插入排序、折半插入排序、歸并排序是穩定的排序算法。
參考:
head只請求頁面的首部。多用來判斷網頁是否被修改和超鏈接的有效性。
get請求頁面信息,并返回實例的主體。
參考:
401:未授權的訪問。
403: 拒絕訪問。
普通的http連接是客戶端連接上服務端,然后結束請求后,由客戶端或者服務端進行http連接的關閉。下次再發送請求的時候,客戶端再發起一個連接,傳送數據,關閉連接。這么個流程反復。但是一旦客戶端發送connection:keep-alive頭給服務端,且服務端也接受這個keep-alive的話,兩邊對上暗號,這個連接就可以復用了,一個http處理完之后,另外一個http數據直接從這個連接走了。減少新建和斷開TCP連接的消耗。這個可以在Nginx設置,
這個keepalive_timout時間值意味著:一個http產生的tcp連接在傳送完最后一個響應后,還需要hold住keepalive_timeout秒后,才開始關閉這個連接。
特別注意TCP層的keep alive和http不是一個意思。TCP的是指:tcp連接建立后,如果客戶端很長一段時間不發送消息,當連接很久沒有收到報文,tcp會主動發送一個為空的報文(偵測包)給對方,如果對方收到了并且回復了,證明對方還在。如果對方沒有報文返回,重試多次之后則確認連接丟失,斷開連接。
tcp的keep alive可通過
net.ipv4.tcp_keepalive_intvl = 75 // 當探測沒有確認時,重新發送探測的頻度。缺省是75秒。
net.ipv4.tcp_keepalive_probes = 9 //在認定連接失效之前,發送多少個TCP的keepalive探測包。缺省值是9。這個值乘以tcp_keepalive_intvl之后決定了,一個連接發送了keepalive之后可以有多少時間沒有回應
net.ipv4.tcp_keepalive_time = 7200 //當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時。一般設置為30分鐘1800
修改:
可以
tcp是面向連接的,upd是無連接狀態的。
udp相比tcp沒有建立連接的過程,所以更快,同時也更安全,不容易被攻擊。upd沒有阻塞控制,因此出現網絡阻塞不會使源主機的發送效率降低。upd支持一對多,多對多等,tcp是點對點傳輸。tcp首部開銷20字節,udp8字節。
udp使用場景:視頻通話、im聊天等。
time-wait表示客戶端等待服務端返回關閉信息的狀態,closed_wait表示服務端得知客戶端想要關閉連接,進入半關閉狀態并返回一段TCP報文。
time-wait作用:
解決辦法:
close_wait:
被動關閉,通常是由于客戶端忘記關閉tcp連接導致。
根據業務來啊~
重要指標是cardinality(不重復數量),這個數量/總行數如果過小(趨近于0)代表索引基本沒意義,比如sex性別這種。
另外查詢不要使用select *,根據select的條件+where條件做組合索引,盡量實現覆蓋索引,避免回表。
僵尸進程:
即子進程先于父進程退出后,子進程的PCB需要其父進程釋放,但是父進程并沒有釋放子進程的PCB,這樣的子進程就稱為僵尸進程,僵尸進程實際上是一個已經死掉的進程。
孤兒進程:
一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,并由init進程對它們完成狀態收集工作。
子進程死亡需要父進程來處理,那么意味著正常的進程應該是子進程先于父進程死亡。當父進程先于子進程死亡時,子進程死亡時沒父進程處理,這個死亡的子進程就是孤兒進程。
但孤兒進程與僵尸進程不同的是,由于父進程已經死亡,系統會幫助父進程回收處理孤兒進程。所以孤兒進程實際上是不占用資源的,因為它終究是被系統回收了。不會像僵尸進程那樣占用ID,損害運行系統。
原文鏈接:
產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
避免方法:
端口占用:lsof -i:端口號 或者 nestat
cpu、內存占用:top
發送信號:kill -l 列出所有信號,然后用 kill [信號變化] [進程號]來執行。如kill -9 453。強制殺死453進程
git log:查看提交記錄
git diff :查看變更記錄
git merge:目標分支改變,而源分支保持原樣。優點:保留提交歷史,保留分支結構。但會有大量的merge記錄
git rebase:將修改拼接到最新,復雜的記錄變得優雅,單個操作變得(revert)很簡單;缺點:
git revert:反做指定版本,會新生成一個版本
git reset:重置到某個版本,中間版本全部丟失
etcd、Consul
pprof
節省空間(非葉子節點不存儲數據,相對b tree的優勢),減少I/O次數(節省的空間全部存指針地址,讓樹變的矮胖),范圍查找方便(相對hash的優勢)。
explain
其他的見:
runtime2.go 中關于 p 的定義: 其中 runnext 指針決定了下一個要運行的 g,根據英文的注釋大致意思是說:
所以當設置 runtime.GOMAXPROCS(1) 時,此時只有一個 P,創建的 g 依次加入 P, 當最后一個即 i==9 時,加入的最后 一個 g 將會繼承當前主 goroutinue 的剩余時間片繼續執行,所以會先輸出 9, 之后再依次執行 P 隊列中其它的 g。
方法一:
方法二:
[圖片上傳失敗...(image-4ef445-1594976286098)]
方法1:to_days,返回給的日期從0開始算的天數。
方法2:data_add。向日期添加指定時間間隔
[圖片上傳失敗...(image-b67b10-1594976286098)]
GO中的defer會在當前函數返回前執行傳入的函數,常用于關閉文件描述符,關閉鏈接及解鎖等操作。
Go語言中使用defer時會遇到兩個常見問題:
接下來我們來詳細處理這兩個問題。
官方有段對defer的解釋:
這里我們先來一道經典的面試題
你覺得這個會打印什么?
輸出結果:
這里是遵循先入后出的原則,同時保留當前變量的值。
把這道題簡化一下:
輸出結果
上述代碼輸出似乎不符合預期,這個現象出現的原因是什么呢?經過分析,我們發現調用defer關鍵字會立即拷貝函數中引用的外部參數,所以fmt.Println(i)的這個i是在調用defer的時候就已經賦值了,所以會直接打印1。
想要解決這個問題也很簡單,只需要向defer關鍵字傳入匿名函數
這里把一些垃圾回收使用的字段忽略了。
中間代碼生成階段cmd/compile/internal/gc/ssa.go會處理程序中的defer,該函數會根據條件不同,使用三種機制來處理該關鍵字
開放編碼、堆分配和棧分配是defer關鍵字的三種方法,而Go1.14加入的開放編碼,使得關鍵字開銷可以忽略不計。
call方法會為所有函數和方法調用生成中間代碼,工作內容:
defer關鍵字在運行時會調用deferproc,這個函數實現在src/runtime/panic.go里,接受兩個參數:參數的大小和閉包所在的地址。
編譯器不僅將defer關鍵字轉成deferproc函數,還會通過以下三種方式為所有調用defer的函數末尾插入deferreturn的函數調用
1、在cmd/compile/internal/gc/walk.go的walkstmt函數中,在遇到ODEFFER節點時會執行Curfn.Func.SetHasDefer(true),設置當前函數的hasdefer屬性
2、在ssa.go的buildssa會執行s.hasdefer = fn.Func.HasDefer()更新hasdefer
3、在exit中會根據hasdefer在函數返回前插入deferreturn的函數調用
runtime.deferproc為defer創建了一個runtime._defer結構體、設置它的函數指針fn、程序計數器pc和棧指針sp并將相關參數拷貝到相鄰的內存空間中
最后調用的return0是唯一一個不會觸發延遲調用的函數,可以避免deferreturn的遞歸調用。
newdefer的分配方式是從pool緩存池中獲取:
這三種方式取到的結構體_defer,都會被添加到鏈表的隊頭,這也是為什么defer按照后進先出的順序執行。
deferreturn就是從鏈表的隊頭取出并調用jmpdefer傳入需要執行的函數和參數。
該函數只有在所有延遲函數都執行后才會返回。
如果我們能夠將部分結構體分配到棧上就可以節約內存分配帶來的額外開銷。
在call函數中有在棧上分配
在運行期間deferprocStack只需要設置一些未在編譯期間初始化的字段,就可以將棧上的_defer追加到函數的鏈表上。
除了分配的位置和堆的不同,其他的大致相同。
Go語言在1.14中通過開放編碼實現defer關鍵字,使用代碼內聯優化defer關鍵的額外開銷并引入函數數據funcdata管理panic的調用,該優化可以將 defer 的調用開銷從 1.13 版本的 ~35ns 降低至 ~6ns 左右。
然而開放編碼作為一種優化 defer 關鍵字的方法,它不是在所有的場景下都會開啟的,開放編碼只會在滿足以下的條件時啟用:
如果函數中defer關鍵字的數量多于8個或者defer處于循環中,那么就會禁用開放編碼優化。
可以看到這里,判斷編譯參數不用-N,返回語句的數量和defer數量的乘積小于15,會啟用開放編碼優化。
延遲比特deferBitsTemp和延遲記錄是使用開放編碼實現defer的兩個最重要的結構,一旦使用開放編碼,buildssa會在棧上初始化大小為8個比特的deferBits
延遲比特中的每一個比特位都表示該位對應的defer關鍵字是否需要被執行。延遲比特的作用就是標記哪些defer關鍵字在函數中被執行,這樣就能在函數返回時根據對應的deferBits確定要執行的函數。
而deferBits的大小為8比特,所以該優化的條件就是defer的數量小于8.
而執行延遲調用的時候仍在deferreturn
這里做了特殊的優化,在runOpenDeferFrame執行開放編碼延遲函數
1、從結構體_defer讀取deferBits,執行函數等信息
2、在循環中依次讀取執行函數的地址和參數信息,并通過deferBits判斷是否要執行
3、調用reflectcallSave執行函數
1、新加入的defer放入隊頭,執行defer時是從隊頭取函數調用,所以是后進先出
2、通過判斷defer關鍵字、return數量來判斷是否開啟開放編碼優化
3、調用deferproc函數創建新的延遲調用函數時,會立即拷貝函數的參數,函數的參數不會等到真正執行時計算
使用go語言的好處: go語言的設計是務實的, go在針對并發上進行了優化, 并且支持大規模高并發, 又由于單一的碼格式, 相比于其他語言更具有可讀性, 在垃圾回收上比java和Python更有效, 因為他是和程序同時執行的.
1. 進程, 線程, 協程的區別, 協程的優勢
2. 講一下GMP模型(重點)
3. Go的GC, 混合寫屏障(重點)
4. go的Slice和數組的區別, slice的擴容原理(重點)
5. 講一下channel,實現原理(重點)
6. 講一下Go的Map的實現原理, 是否線程安全, 如何實現安全(重點)
7. new 和 make 的區別
8. 說一下內存逃逸
9. 函數傳指針和傳值有什么區別
10. goroutine之間的通信方式
11. 測試是怎么做的(單元測試, 壓力測試)
12. 堆和棧的區別
請實現 個算法,確定 個字符串的所有字符【是否全都不同】。這 我們要求【不允
許使 額外的存儲結構】。 給定 個string,請返回 個bool值,true代表所有字符全都
不同,false代表存在相同的字符。 保證字符串中的字符為【ASCII字符】。字符串的
度 于等于【3000】。
這 有 個重點,第 個是 ASCII字符 , ASCII字符 字符 共有256個,其中128個是常
字符,可以在鍵盤上輸 。128之后的是鍵盤上 法找到的。
然后是全部不同,也就是字符串中的字符沒有重復的,再次,不準使 額外的儲存結
構,且字符串 于等于3000。
如果允許其他額外儲存結構,這個題 很好做。如果不允許的話,可以使 golang內置
的 式實現。
通過 strings.Count 函數判斷:
使 的是golang內置 法 strings.Count ,可以 來判斷在 個字符串中包含
的另外 個字符串的數量
還有不同的方法同樣可以實現,你了解嗎?
推薦go相關技術 專欄
gRPC-go源碼剖析與實戰_帶你走進gRPC-go的源碼世界-CSDN博客
分享標題:go語言面試實踐題 go算法面試題
本文URL:http://m.newbst.com/article10/dodhggo.html
成都網站建設公司_創新互聯,為您提供標簽優化、網站制作、品牌網站設計、建站公司、Google、移動網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯