go中的goroutine是go語言在語言層級支援並發的一種特性。初接觸go的時候對go的goroutine的歡喜至極,實現並發簡到簡直bt的地步。
但是在專案過程中,越來越發現goroutine是一個很容易被大家濫用的東西。 goroutine是一把雙面刃。這裡列舉goroutine所使用的幾宗罪:
##1、goroutine的指標傳遞是不安全的
fun main() { request := request.NewRequest() //这里的NewRequest()是传递回一个type Request的指针 go saveRequestToRedis1(request) go saveReuqestToRedis2(request) select{} }非常符合邏輯的程式碼:主routine開一個routine把request傳遞給saveRequestToRedis1,讓它把請求儲存到redis節點1中同時開另一個routine把request傳遞給saveReuqestToRedis2,讓它把請求儲存到redis節點2中然後主routine就進入循環(不結束進程)#問題現在來了,saveRequestToRedis1和saveReuqestToRedis2兩個函數其實不是我寫的,而是團隊另一個人寫的,我對其中的實現一無所知,也不想去仔細看內部的具體實現。但是根據函數名,我想當然地把request指標傳進去。 實際上saveRequestToRedis1和saveRequestToRedis2 是這樣實現的:
func saveRequestToRedis1(request *Request){ … request.ToUsers = []int{1,2,3} //这里是一个赋值操作,修改了request指向的数据结构 … redis.Save(request) return }這樣有什麼問題? saveRequestToRedis1和saveReuqestToRedis2兩個goroutine修改了同一個共享資料結構,但是由於routine的執行是無序的,因此我們無法保證request.ToUsers設定和redis.Save()是一個原子操作,這樣就會出現實際儲存redis的數據錯誤的bug。 好吧,你可以說這個saveRequestToRedis的函數實現的有問題,沒有考慮到會是使用go routine呼叫。請再想一想,這個saveRequestToRedis的具體實作是沒有任何問題的,它不應該考慮上層是怎麼使用它的。 那就是我的goroutine的使用有問題,主routine在開一個routine的時候並沒有確認這個routine裡面的任何一句程式碼有沒有修改了主routine中的資料。對的,主routine確實需要考慮這個情況。 主goroutine在啟用go routine的時候需要閱讀子routine中的每行程式碼來決定是否有修改共享資料? ?這在實際專案開發過程中是多麼降低開發速度的一件事情啊! go語言使用goroutine是想減輕並發的開發壓力,卻不曾想是在另一方面增加了開發壓力。 上面說的那麼多,就是想得出一個結論:gorotine的指標傳遞是不安全的! ! 如果上一個例子還不夠隱蔽,這裡還有一個例子:
fun (this *Request)SaveRedis() { redis1 := redis.NewRedisAddr("xxxxxx") redis2 := redis.NewRedisAddr("xxxxxx") go this.saveRequestToRedis(redis1) go this.saveRequestToRedis(redis2) select{} }很少人會考慮到this指標指向的物件是否會有問題,這裡的this指標傳遞給routine應該說是非常隱密的。
2、goroutine增加了函數的危險係數
這一點其實也是源自於上面一點。上文說,往一個go函數中傳遞指標是不安全的。那麼換個角度想,你怎麼能保證你要呼叫的函數在函數實作內部不會使用go呢?如果不去看函數體內部具體實現,是沒有辦法確定的。 例如我們將上面的典型例子稍微改改func main() { request := request.NewRequest() saveRequestToRedis1(request) saveRequestToRedis2(request) select{} }這下我們沒有使用並發,就一定不會出現這問題了吧?追到函數裡面去,傻眼了:
func saveReqeustToRedis1(request *Request) { … go func() { … request.ToUsers = []{1,2,3} …. redis.Save(request) } }裡面起了一個goroutine,並修改了request指標所指向的物件。這裡就產生了錯誤了。好吧,如果在呼叫函數的時候,不看函數內部的具體實現,這個問題就無法避免。 所以說,從最壞的思考角度出發,每個呼叫函數理論上來說都是不安全的!試想一下,這個呼叫函數如果不是自己開發群組的人寫的,而是使用網路上的第三方開源程式碼...確實無法想像找出這個bug要花費多少時間。
3、goroutine的濫用陷阱
#看一下這個例子:func main() { go saveRequestToRedises(request) } func saveRequestToRedieses(request *Request) { for _, redis := range Redises { go redis.saveRequestToRedis(request) } } func saveRequestToRedis(request *Request) { …. go func() { request.ToUsers = []{1,2,3} … redis.Save(request) } }神奇啊,go無所不在,好像眨眨眼就在哪裡冒出來了。這就是go的濫用,到處都見到go,但是卻不是很明確,哪裡該用go?為什麼用go? goroutine確實會有效率的提升? c語言的並發比go語言的並發複雜和繁瑣地多,因此我們在使用之前會深思,考慮使用並發獲得的好處和壞處。
處理方法
下面說幾個我處理這些問題的方法:1、當啟動一個goroutine的時候,如果一個函數必須要傳遞一個指針,但是函數層級很深,在無法保證安全的情況下,傳遞這個指針指向物件的一個克隆,而不是直接傳遞指針
fun main() { request := request.NewRequest() go saveRequestToRedis1(request.Clone()) go saveReuqestToRedis2(request.Clone()) select{} }Clone函數需要另外寫。可以在結構體定義之後簡單跟上這個方法。如:
func (this *Request)Clone(){ newRequest := NewRequst() newRequest.ToUsers = make([]int, len(this.ToUsers)) copy(newRequest.ToUsers, this.ToUsers) }
其实从效率角度考虑这样确实会产生不必要的Clone的操作,耗费一定内存和CPU。但是在我看来,首先,为了安全性,这个尝试是值得的。
其次,如果项目对效率确实有很高的要求,那么你不妨在开发阶段遵照这个原则使用clone,然后在项目优化阶段,作为一种优化手段,将不必要的Clone操作去掉。这样就能在保证安全的前提下做到最好的优化。
2、什么时候使用go的问题
有两种思维逻辑会想到使用goroutine:
1 业务逻辑需要并发
比如一个服务器,接收请求,阻塞式的方法是一个请求处理完成后,才开始第二个请求的处理。其实在设计的时候我们一定不会这么做,我们会在一开始就已经想到使用并发来处理这个场景,每个请求启动一个goroutine为它服务,这样就达到了并行的效果。这种goroutine直接按照思维的逻辑来使用goroutine
2 性能优化需要并发
一个场景是这样:需要给一批用户发送消息,正常逻辑会使用
for _, user := range users { sendMessage(user) }
但是在考虑到性能问题的时候,我们就不会这样做,如果users的个数很大,比如有1000万个用户?我们就没必要将1000万个用户放在一个routine中运行处理,考虑将1000万用户分成1000份,每份开一个goroutine,一个goroutine分发1万个用户,这样在效率上会提升很多。这种是性能优化上对goroutine的需求
按照项目开发的流程角度来看。在项目开发阶段,第一种思路的代码实现会直接影响到后续的开发实现,因此在项目开发阶段应该马上实现。
但是第二种,项目中是由很多小角落是可以使用goroutine进行优化的,但是如果在开发阶段对每个优化策略都考虑到,那一定会直接打乱你的开发思路,会让你的开发周期延长,而且很容易埋下潜在的不安全代码。
因此第二种情况在开发阶段绝不应该直接使用goroutine,而该在项目优化阶段以优化的思路对项目进行重构。
推荐:golang开发栏目
以上是go語言中goroutine的使用詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

goisidealforbuildingscalablesystemsduetoitssimplicity,效率和建築物內currencysupport.1)go'scleansyntaxandaxandaxandaxandMinimalisticDesignenhanceProductivityAndRedCoductivityAndRedCuceErr.2)ItSgoroutinesAndInesAndInesAndInesAndineSandChannelsEnablenableNablenableNableNablenableFifficConcurrentscorncurrentprogragrammentworking torkermenticmminging

Initfunctionsingorunautomationbeforemain()andareusefulforsettingupenvorments和InitializingVariables.usethemforsimpletasks,避免使用輔助效果,andbecautiouswithTestingTestingTestingAndLoggingTomaintAnainCodeCodeCodeClarityAndTestesto。

goinitializespackagesintheordertheordertheyimported,thenexecutesInitFunctionswithinApcageIntheirdeFinityOrder,andfilenamesdetermineTheOrderAcractacractacrosmultiplefiles.thisprocessCanbeCanbeinepessCanbeInfleccessByendercrededBydeccredByDependenciesbetenciesbetencemendencenciesbetnependendpackages,whermayleLeadtocomplexinitialitialializizesizization

CustomInterfacesingoarecrucialforwritingFlexible,可維護,andTestableCode.TheyEnableDevelostOverostOcusonBehaviorBeiroveration,增強ModularityAndRobustness.byDefiningMethodSigntulSignatulSigntulSignTypaterSignTyperesthattypesmustemmustemmustemmustemplement,InterfaceSallowForCodeRepodEreusaperia

使用接口進行模擬和測試的原因是:接口允許定義合同而不指定實現方式,使得測試更加隔離和易於維護。 1)接口的隱式實現使創建模擬對像變得簡單,這些對像在測試中可以替代真實實現。 2)使用接口可以輕鬆地在單元測試中替換服務的真實實現,降低測試複雜性和時間。 3)接口提供的靈活性使得可以為不同測試用例更改模擬行為。 4)接口有助於從一開始就設計可測試的代碼,提高代碼的模塊化和可維護性。

在Go中,init函數用於包初始化。 1)init函數在包初始化時自動調用,適用於初始化全局變量、設置連接和加載配置文件。 2)可以有多個init函數,按文件順序執行。 3)使用時需考慮執行順序、測試難度和性能影響。 4)建議減少副作用、使用依賴注入和延遲初始化以優化init函數的使用。

go'SselectStatementTreamLinesConcurrentProgrambyMultiplexingOperations.1)itallowSwaitingOnMultipleChannEloperations,執行thefirstreadyone.2)theDefirstreadyone.2)thedefefcasepreventlocksbysbysbysbysbysbythoplocktrograpraproxrograpraprocrecrecectefnoopeready.3)

contextancandwaitgroupsarecrucialingoformanaginggoroutineseflect.1)context contextsallowsAllowsAllowsAllowsAllowsAllingCancellationAndDeadLinesAcrossapibiboundaries,確保GoroutinesCanbestoppedGrace.2)WaitGroupsSynChronizeGoroutines,確保Allimizegoroutines,確保AllizeNizeGoROutines,確保AllimizeGoroutines


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

Atom編輯器mac版下載
最受歡迎的的開源編輯器