欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談GO中的Channel以及死鎖的造成

 更新時間:2022年03月18日 16:02:16   作者:Sirius_7  
本文主要介紹了淺談GO中的Channel以及死鎖的造成,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下

寫在前面

這篇文章的誕生要感謝MIT 6.284課程。在其中一節(jié)課中,談到了多線程的協(xié)同的一些問題,其中就涉及到了channel這個概念,并由一段代碼引發(fā)思考并逐漸深入得到了這篇文章。

引子

課程中有一段代碼如下:

在這里插入圖片描述

其大致含義是:代碼背景是在進行多線程網(wǎng)絡爬蟲頁面url,master線程啟動后,從channel通道中讀取當前頁面的所有url即urls,接著再對這個urls中的每一個url進行爬蟲讀取新頁面中的urls(即執(zhí)行go worker(u, ch ,fetcher)),每啟動一個worker線程便開始向channel中寫入該url指向頁面中所有包含的urls,以供master線程讀取。

問題拋出

那么問題來了,為什么第一層for循環(huán)不會range完ch之后便直接結(jié)束循環(huán),還需要利用局部變量n來根據(jù)特定情況跳出循環(huán)?

問題解釋

課程上的解釋是,這個range會一直阻塞,但并未提出解釋。其實,這里很容易分析,因為當前的channel是一個無緩沖通道。所謂無緩沖通道,簡單的講就是兩個線程對channel進行操作,一個讀,一個寫,永遠都只能是寫一個,讀一個按照這樣的順序進行。更詳細一些的話,讀的那個線程會一直阻塞,直到寫的線程向channel中寫入一個數(shù)據(jù)。反之亦然,寫的線程在完成一次寫操作之后,也會一直阻塞直到另外一個線程完成對該channel的讀取操作。上述情況只有一種例外狀況,那就是該channel通道被某個線程close掉了:close(channel)。

而這里的range其實不太等同于對數(shù)組的range,這里的range實質(zhì)上為對channel通道的讀取。所以,在并未有認為close通道的前提下,該for循環(huán)會一直阻塞,不會退出,于是需要設定一個局部狀態(tài)量n讓其退出循環(huán),保證程序的正常運行。當然我們也可以通過close其channel來實現(xiàn),不過我認為close的時機可能不是非常容易把握。

繼續(xù)深入

完成上述思考之后,對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")
}

執(zhí)行結(jié)果直接報錯,顯示:fatal error: all goroutines are asleep - deadlock!
即:出現(xiàn)死鎖。
為什么會出現(xiàn)這種情況?
首先我們來分析一下這段代碼的目的:利用channel通道,實現(xiàn)數(shù)據(jù)的傳遞,一個線程向channel通道中寫入數(shù)據(jù),另外一個讀取。為什么會出現(xiàn)死鎖呢?

首先我們分析一下當前程序有多少個線程在執(zhí)行,main函數(shù)是主線程,調(diào)用test函數(shù)之后,主線程進入了test函數(shù)中繼續(xù)運行。而在test函數(shù)中,采用閉包函數(shù)或者說匿名函數(shù)的方法新開了一個線程,即goroutine去向已經(jīng)生成的無緩沖通道中發(fā)送數(shù)據(jù)。發(fā)送的過程并非是主線程的任務,所以主線程在執(zhí)行完go func之后馬上跳過繼續(xù)執(zhí)行下面的for循環(huán),也就是要將channel中的數(shù)據(jù)讀取出來。

for a := range ch {
		fmt.Print(a)
	}

這時,問題來了。現(xiàn)在兩個線程,主線程讀,另外一個寫。在另外一個線程完成最后一個寫之后,主線程開始阻塞等待新的寫操作,而主線程一旦阻塞整個test函數(shù)也無法結(jié)束,所以導致了死鎖的產(chǎn)生,主線程一直被阻塞。

明白了上述原因之后,解決方法便很簡單了,將從channel中讀數(shù)據(jù)的任務交給另外一個線程,而非主線程,主線程直接調(diào)用完test函數(shù)之后馬上結(jié)束,其他兩個線程的死活都不會影響到程序本身的運行,即主線程的運行。如下:

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")
}

當然這種方法是偷懶的,這樣的操作有可能導致內(nèi)存溢出等情況發(fā)生,所以最好還是讓發(fā)送數(shù)據(jù)的線程在發(fā)送完之后將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語言對多線程天然的集成性,讓其在處理并發(fā)的一些事務時十分方便,但是還是需要注意一些死鎖的生成。

到此這篇關于淺談GO中的Channel以及死鎖的造成的文章就介紹到這了,更多相關GO中Channel及死鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • go語言-在mac下brew升級golang

    go語言-在mac下brew升級golang

    這篇文章主要介紹了go語言-在mac下brew升級golang,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go設計模式之單例模式圖文詳解

    Go設計模式之單例模式圖文詳解

    單例模式是一種創(chuàng)建型設計模式,讓你能夠保證一個類只有一個實例,并提供一個訪問該實例的全局節(jié)點,本文就通過圖文給大家介紹一下Go的單例模式,需要的朋友可以參考下
    2023-07-07
  • Golang 使用map需要注意的幾個點

    Golang 使用map需要注意的幾個點

    這篇文章主要介紹了Golang 使用map需要注意的幾個點,幫助大家更好的理解和學習golang,感興趣的朋友可以了解下
    2020-09-09
  • 一文詳解Go中方法接收器的選擇

    一文詳解Go中方法接收器的選擇

    許多 Go 初學者在方法接收器的選擇上可能會感到困惑,不知道該選擇值接收器還是指針接收器。本文將會對方法接收器進行介紹,并給出如何選擇正確方法接收器的指導建議,希望對大家有所幫助
    2023-04-04
  • golang 結(jié)構體初始化時賦值格式介紹

    golang 結(jié)構體初始化時賦值格式介紹

    這篇文章主要介紹了golang 結(jié)構體初始化時賦值格式介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • go語言通過反射獲取和設置結(jié)構體字段值的方法

    go語言通過反射獲取和設置結(jié)構體字段值的方法

    這篇文章主要介紹了go語言通過反射獲取和設置結(jié)構體字段值的方法,實例分析了Go語言反射的使用技巧,需要的朋友可以參考下
    2015-03-03
  • Golang的命名規(guī)范及最佳實踐(推薦!)

    Golang的命名規(guī)范及最佳實踐(推薦!)

    這篇文章主要給大家介紹了關于Golang的命名規(guī)范及最佳實踐的相關資料,命名規(guī)則涉及變量、常量、全局函數(shù)、結(jié)構、接口、方法等的命名,文中介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • GoLang sync.Pool簡介與用法

    GoLang sync.Pool簡介與用法

    這篇文章主要介紹了GoLang sync.Pool簡介與用法,Pool是可伸縮、并發(fā)安全的臨時對象池,用來存放已經(jīng)分配但暫時不用的臨時對象,通過對象重用機制,緩解GC壓力,提高程序性能
    2023-01-01
  • 詳解Golang中string的實現(xiàn)原理與高效使用

    詳解Golang中string的實現(xiàn)原理與高效使用

    在Go語言中,無論是字符串常量、字符串變量還是代碼中出現(xiàn)的字符串字面量,它們的類型都被統(tǒng)一設置為string,下面就跟隨小編一起來了解一下Golang中string的實現(xiàn)原理與高效使用吧
    2024-01-01
  • Golang中對json的優(yōu)雅處理方式

    Golang中對json的優(yōu)雅處理方式

    這篇文章主要給大家介紹了關于Golang中對json的優(yōu)雅處理方式,解析JSON在golang中很麻煩,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-06-06

最新評論