Go 由于不支持泛型而臭名昭著,但最近棚基,泛型已接近成為現(xiàn)實(shí)。Go 團(tuán)隊(duì)實(shí)施了一個(gè)看起來比較穩(wěn)定的設(shè)計(jì)草案,并且正以源到源翻譯器原型的形式獲得關(guān)注。本文講述的是泛型的最新設(shè)計(jì),以及如何自己嘗試泛型。
主要從事網(wǎng)頁設(shè)計(jì)、PC網(wǎng)站建設(shè)(電腦版網(wǎng)站建設(shè))、wap網(wǎng)站建設(shè)(手機(jī)版網(wǎng)站建設(shè))、成都響應(yīng)式網(wǎng)站建設(shè)、程序開發(fā)、微網(wǎng)站、微信平臺(tái)小程序開發(fā)等,憑借多年來在互聯(lián)網(wǎng)的打拼,我們?cè)诨ヂ?lián)網(wǎng)網(wǎng)站建設(shè)行業(yè)積累了豐富的成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、網(wǎng)絡(luò)營銷經(jīng)驗(yàn),集策劃、開發(fā)、設(shè)計(jì)、營銷、管理等多方位專業(yè)化運(yùn)作于一體,具備承接不同規(guī)模與類型的建設(shè)項(xiàng)目的能力。
例子
FIFO Stack
假設(shè)你要?jiǎng)?chuàng)建一個(gè)先進(jìn)先出堆棧。沒有泛型,你可能會(huì)這樣實(shí)現(xiàn):
type?Stack?[]interface{}func?(s?Stack)?橘和銷Peek()?interface{}?{
return?s[len(s)-1]
}
func?(s?*Stack)?Pop()?{
*s?=?(*s)[:
len(*s)-1]
}
func?(s?*Stack)?Push(value?interface{})?{
*s?=?
append(*s,?value)
}
但是,這里存在一個(gè)問題:每當(dāng)你 Peek 項(xiàng)時(shí),都必須使用類型斷言將其從 interface{} 轉(zhuǎn)換為你需要的類型。如果你的堆棧是 *MyObject 的堆棧,則意味著很多 s.Peek().(*MyObject)這樣的代碼。這不僅讓人眼花繚亂,而且還可能引發(fā)錯(cuò)誤。比如忘記 * 怎么辦?或者如果您輸入錯(cuò)誤的類型怎么辦?s.Push(MyObject{})` 可以順利編譯,而且你可能不會(huì)發(fā)現(xiàn)到自己的錯(cuò)誤,直到它影響到你的整個(gè)服務(wù)為止。
通常,使用 interface{} 是相對(duì)危險(xiǎn)的。使用更多受限制的類型總是更安全,因?yàn)榭梢栽诰幾g時(shí)而不是運(yùn)行時(shí)發(fā)現(xiàn)問題。
泛型通過允許類型具有類型參數(shù)來解決此問題:
type?Stack(type?T)?[]Tfunc?(s?Stack(T))?Peek()?T?{
return?s[len(s)-1]
}
func?(s?*Stack(T))?Pop()?{
*s?=?(*s)[:
len(*s)-1]
}
func?(s?*Stack(T))?Push(value?T)?{
*s?=?
append(*s,?value)
}
這會(huì)向 Stack 添加一個(gè)類型參數(shù),從而完全不需要 interface{}。現(xiàn)在,當(dāng)你使用 Peek() 時(shí),返回的值已經(jīng)是原始類型,并且沒有機(jī)會(huì)返回錯(cuò)誤的值類型。這種方式更安全,更容易使用。(譯注:就是看起來更丑陋,^-^)
此外,泛型代碼通常更易于編譯器優(yōu)化,從而獲得更好的性能(以二進(jìn)制大小為代價(jià))。如果我們對(duì)上面的非泛型代碼和泛型代碼進(jìn)行基準(zhǔn)測(cè)試,我們可以看到區(qū)別:
type?MyObject?struct?{
X?
int
}
var?sink?MyObjectfunc?BenchmarkGo1(b?*testing.B)?{
for?i?:=?0;?i??b.N;?i++?{
var?s?Stack
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink?=?s.Peek().(MyObject)
}
}
func?BenchmarkGo2(b?*testing.B)?{
for?i?:=?0;?i??b.N;?i++?{
var?s?Stack(MyObject)
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink?=?s.Peek()
}
}
結(jié)果:
BenchmarkGo1BenchmarkGo1-16?????12837528?????????87.0?ns/op???????48?B/op????????2?allocs/opBenchmarkGo2BenchmarkGo2-16?????28406479?????????41.9?ns/op???????24?B/op????????2?allocs/op
在這種情況下,我們分配更少圓游的內(nèi)存,同時(shí)泛型的速度是非泛型的兩倍。
合約(Contracts)
上面的堆棧示例適用于任何類型。但是,在許多情況下,你需要編寫僅適用于具有某些特征的類型的代碼。例如,你可能希望堆棧要求類型實(shí)現(xiàn) String() 函數(shù)
在前一小節(jié)中介紹了點(diǎn)亮第一個(gè)LED燈,這里我們準(zhǔn)備進(jìn)階嘗試下,輸出第一段PWM波形。(PWM也就是脈寬調(diào)制,一譽(yù)宴種可調(diào)占空比的技術(shù),得到的效果就是:如果用示波器測(cè)量引腳會(huì)發(fā)現(xiàn)有方波輸出,而且高電平、低電平的時(shí)間是可調(diào)的。)
這里爪爪熊準(zhǔn)備寫成一個(gè)golang的庫,并開源到github上,后續(xù)更新將直接更新到github中,如果你有興趣可以和我聯(lián)系。 github點(diǎn)抗 /dpawsbear/bear_rpi_go
我在很多的教程中都看到說樹莓派的PWM(硬件)只有一個(gè)GPIO能夠輸出,就是 GPIO1 。這可是不小的打擊,因?yàn)槲蚁胧褂弥辽偎膫€(gè) PWM ,還是不死心,想通過硬件手冊(cè)上找尋蛛絲馬跡,看看究竟怎么回事。
手冊(cè)上找尋東西稍等下講述,這里先提供一種方法測(cè)試 樹莓派3B 的 PWM 方法:用指令控制硬件PWM。
這里通過指令的方式掌握了基本的pwm設(shè)置技巧,決定去翻一下手冊(cè)看看到底PWM怎么回事,這里因?yàn)闆]有 BCM2837 的手冊(cè),根據(jù)之前文章引用官網(wǎng)所說, BCM2835 和 BCM2837 應(yīng)該是一樣的。這里我們直接翻閱 BCM2835 的手冊(cè),直接找到 PWM 章節(jié)。找到了如下圖:
圖中可以看到在博通的命名規(guī)則中 GPIO 12、13、18、19、40、41、45、52、53 均可以作為PWM輸出。但是只有兩路PWM0 PWM1。根據(jù)我之前所學(xué)知識(shí),不出意外應(yīng)該是PWM0 和 PWM1可以輸出不一樣的占空比,但是頻率應(yīng)該是一樣的。因?yàn)闆]有示波器,暫時(shí)不好測(cè)試。先找到下面對(duì)應(yīng)圖:
根據(jù)以上兩個(gè)圖對(duì)比可以發(fā)現(xiàn)如下規(guī)律:
對(duì)照上面的表可以看出從 BCM2837 中印出來的能夠使用在PWM上的就這幾個(gè)了。
為了驗(yàn)證個(gè)人猜想是否正確,這里先直接使用指令的模式,模擬配置下是否能夠正常輸出。
通過上面一系列指令模擬發(fā)現(xiàn),(GPIO1、GPIO26)、(GPIO23、GPIO24)是綁定在一起的,調(diào)節(jié)任意一個(gè),另外一個(gè)也會(huì)發(fā)生變化。也即是PWM0、PWM1雖然輸出了兩路,可以理解成孫宴兩路其實(shí)都是連在一個(gè)輸出口上。這里由于沒有則虛銀示波器或者邏輯分析儀這類設(shè)備(僅有一個(gè)LED燈),所以測(cè)試很簡陋,下一步是使用示波器這類東西對(duì)頻率以及信號(hào)穩(wěn)定性進(jìn)行下測(cè)試。
小節(jié):樹莓派具有四路硬件輸出PWM能力,但是四路中只能輸出兩個(gè)獨(dú)立(占空比獨(dú)立)的PWM,同時(shí)四路輸出的頻率均是恒定的。
上面大概了解清楚了樹莓派3B的PWM結(jié)構(gòu),接下來就是探究如何使用Go語言進(jìn)行設(shè)置。
因?yàn)槟玫搅耸謨?cè),這里我想直接操作寄存器的方式進(jìn)行設(shè)置,也是順便學(xué)習(xí)下Go語言處理寄存器的過程。首先需要拿到pwm 系列寄存器的基地址,但是翻了一圈手冊(cè),發(fā)現(xiàn)只有偏移,沒有找到基地址。
經(jīng)過了一段時(shí)間的努力后,決定寫一個(gè) 樹莓派3B golang包開源放在github上,只需要寫相關(guān)程序進(jìn)行調(diào)用就可以了,以下是相關(guān)demo(pwm)(在GPIO.12 上輸出PWM波,放上LED燈會(huì)有呼吸燈的效果,具體多少頻率還沒有進(jìn)行測(cè)試)
以下是demo(pwm) 源碼
內(nèi)核線程(Kernel-Level Thread ,KLT)
輕量級(jí)進(jìn)程(Light Weight Process,LWP):輕量級(jí)進(jìn)程就是我們通常意義上所講的線程,由于每個(gè)輕量級(jí)進(jìn)程都由一個(gè)內(nèi)核線程支持,因此只有先支持內(nèi)核線程,才能有輕量級(jí)進(jìn)程
用戶線程與系統(tǒng)線程一一對(duì)應(yīng),用戶線程執(zhí)行如lo操作的系統(tǒng)調(diào)用時(shí),來回切換操作開銷相對(duì)比較大
多個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程,當(dāng)內(nèi)核線程對(duì)應(yīng)的一個(gè)用戶線程被阻塞掛起時(shí)候,其他用戶線程也阻塞不能執(zhí)行了。
多對(duì)多模型是可以充分利用多核CPU提升運(yùn)行效能的
go線程模型包含三個(gè)概念:內(nèi)核線程(M),goroutine(G),G的上下文環(huán)境(P);
GMP模型是goalng特有的。
P與M一般是一一對(duì)應(yīng)的。P(上下文)管理著一組G(goroutine)掛載在M(內(nèi)核線程)上運(yùn)行,圖中左邊藍(lán)色為正在執(zhí)行狀態(tài)的goroutine,右邊為待執(zhí)行狀態(tài)的goroutiine隊(duì)列。P的數(shù)量由環(huán)境變量GOMAXPROCS的值或程序運(yùn)行runtime.GOMAXPROCS()進(jìn)行設(shè)置。
當(dāng)一個(gè)os線程在執(zhí)行M1一個(gè)G1發(fā)生阻塞時(shí),調(diào)度器讓M1拋棄P,等待G1返回,然后另起一個(gè)M2接收P來執(zhí)行剩下的goroutine隊(duì)列(G2、G3...),這是golang調(diào)度器厲害的地方,可以保證有足夠的線程來運(yùn)行剩下所有的goroutine。
當(dāng)G1結(jié)束后,M1會(huì)重新拿回P來完成,如果拿不到就丟到全局runqueue中,然后自己放到線程池或轉(zhuǎn)入休眠狀態(tài)??臻e的上下文P會(huì)周期性的檢查全局runqueue上的goroutine,并且執(zhí)行它。
另一種情況就是當(dāng)有些P1太閑而其他P2很忙碌的時(shí)候,會(huì)從其他上下文P2拿核薯如一些G來執(zhí)行。
詳細(xì)可以翻看下方第一個(gè)參考鏈接,寫得真好。
最后用大佬的總結(jié)來做最后的收尾————
Go語言運(yùn)行時(shí),通過核心元素G,M,P 和 自己的調(diào)度器,實(shí)現(xiàn)了自己的并發(fā)線程模型。調(diào)度器通過對(duì)G,M,P的調(diào)度實(shí)現(xiàn)了兩級(jí)線程模型中操作系統(tǒng)內(nèi)核之外的調(diào)度任務(wù)。手滑整個(gè)調(diào)度過程中會(huì)在多種時(shí)機(jī)去觸發(fā)最核心的步驟 “一整輪調(diào)度”,而一整輪調(diào)度中最關(guān)鍵的部分在“全力查找可運(yùn)行G”,它保證了M的高效運(yùn)行(換句話說就是充分使用了計(jì)算機(jī)的物理資源),一整輪調(diào)度中還會(huì)涉及到M的啟用停改啟止。最后別忘了,還有一個(gè)與Go程序生命周期相同的系統(tǒng)監(jiān)測(cè)任務(wù)來進(jìn)行一些輔助性的工作。
淺析Golang的線程模型與調(diào)度器
Golang CSP并發(fā)模型
Golang線程模型
網(wǎng)站欄目:go語言找色 go語言chan
本文地址:http://www.rwnh.cn/article12/dccpjdc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、搜索引擎優(yōu)化、品牌網(wǎng)站建設(shè)、全網(wǎng)營銷推廣、小程序開發(fā)、用戶體驗(yàn)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)