<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
這篇文章的誕生要感謝MIT 6.284課程。在其中一節課中,談到了多執行緒的協同的一些問題,其中就涉及到了channel這個概念,並由一段程式碼引發思考並逐漸深入得到了這篇文章。
課程中有一段程式碼如下:
其大致含義是:程式碼背景是在進行多執行緒網路爬蟲頁面url,master執行緒啟動後,從channel通道中讀取當前頁面的所有url即urls,接著再對這個urls中的每一個url進行爬蟲讀取新頁面中的urls(即執行go worker(u, ch ,fetcher)),每啟動一個worker執行緒便開始向channel中寫入該url指向頁面中所有包含的urls,以供master執行緒讀取。
那麼問題來了,為什麼第一層for迴圈不會range完ch之後便直接結束迴圈,還需要利用區域性變數n來根據特定情況跳出迴圈?
課程上的解釋是,這個range會一直阻塞,但並未提出解釋。其實,這裡很容易分析,因為當前的channel是一個無緩衝通道。所謂無緩衝通道,簡單的講就是兩個執行緒對channel進行操作,一個讀,一個寫,永遠都只能是寫一個,讀一個按照這樣的順序進行。更詳細一些的話,讀的那個執行緒會一直阻塞,直到寫的執行緒向channel中寫入一個資料。反之亦然,寫的執行緒在完成一次寫操作之後,也會一直阻塞直到另外一個執行緒完成對該channel的讀取操作。上述情況只有一種例外狀況,那就是該channel通道被某個執行緒close掉了:close(channel)。
而這裡的range其實不太等同於對陣列的range,這裡的range實質上為對channel通道的讀取。所以,在並未有認為close通道的前提下,該for迴圈會一直阻塞,不會退出,於是需要設定一個區域性狀態量n讓其退出迴圈,保證程式的正常執行。當然我們也可以通過close其channel來實現,不過我認為close的時機可能不是非常容易把握。
完成上述思考之後,對channel進行了較為的深入的分析,當然分析是以具體的實驗展開的。給出下述實驗程式碼:
func main() { test() } func test() { ch := make(chan int,4) go func() { ch <- 1 ch <- 2 ch <- 3 ch <- 4 }() //go func() { for a := range ch { fmt.Print(a) } //}() fmt.Print("test is over") }
執行結果直接報錯,顯示:fatal error: all goroutines are asleep - deadlock!
即:出現死鎖。
為什麼會出現這種情況?
首先我們來分析一下這段程式碼的目的:利用channel通道,實現資料的傳遞,一個執行緒向channel通道中寫入資料,另外一個讀取。為什麼會出現死鎖呢?
首先我們分析一下當前程式有多少個執行緒在執行,main函數是主執行緒,呼叫test函數之後,主執行緒進入了test函數中繼續執行。而在test函數中,採用閉包函數或者說匿名函數的方法新開了一個執行緒,即goroutine去向已經生成的無緩衝通道中傳送資料。傳送的過程並非是主執行緒的任務,所以主執行緒在執行完go func之後馬上跳過繼續執行下面的for迴圈,也就是要將channel中的資料讀取出來。
for a := range ch { fmt.Print(a) }
這時,問題來了。現在兩個執行緒,主執行緒讀,另外一個寫。在另外一個執行緒完成最後一個寫之後,主執行緒開始阻塞等待新的寫操作,而主執行緒一旦阻塞整個test函數也無法結束,所以導致了死鎖的產生,主執行緒一直被阻塞。
明白了上述原因之後,解決方法便很簡單了,將從channel中讀資料的任務交給另外一個執行緒,而非主執行緒,主執行緒直接呼叫完test函數之後馬上結束,其他兩個執行緒的死活都不會影響到程式本身的執行,即主執行緒的執行。如下:
func main() { test() } func test() { ch := make(chan int,4) go func() { ch <- 1 ch <- 2 ch <- 3 ch <- 4 }() go func() { for a := range ch { fmt.Print(a) } }() fmt.Print("test is over") }
當然這種方法是偷懶的,這樣的操作有可能導致記憶體溢位等情況發生,所以最好還是讓傳送資料的執行緒在傳送完之後將channel關閉,如下所示:
func main() { test() time.Sleep(time.Second) } func test() { ch := make(chan int,4) go func() { ch <- 1 ch <- 2 ch <- 3 ch <- 4 close(ch) }() go func() { for a := range ch { fmt.Print(a) } }() fmt.Print("test is over") }
輸出為:
test is over1234
注意,這裡為了保證能夠輸出1234,需要將主執行緒休眠1s,確保主執行緒在退出之前,負責讀取的執行緒能夠完成讀取工作。
Go語言對多執行緒天然的整合性,讓其在處理並行的一些事務時十分方便,但是還是需要注意一些死鎖的生成。
到此這篇關於淺談GO中的Channel以及死鎖的造成的文章就介紹到這了,更多相關GO中Channel及死鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45