<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                正如我們在“Goroutines”一節中介紹的那樣,goroutines占用資源較少且易于創建。運行時將多個goroutine復用到任意數量的操作系統線程,以便我們不必擔心抽象級別。但是他們會花費成本資源,并且goroutine不會被運行時垃圾收集,所以無論內存占用多少,我們都不想讓他們對我們的進程撒謊。 那么我們如何去確保他們被清理干凈? 讓我們從頭開始,一步一步思考:為什么會有一個goroutine? 在第二章中,我們確定,goroutines代表可能并行或不可以并行運行的工作單元。 該goroutine有幾條路徑終止: * 當它完成任務。 * 當它遇到不可恢復的錯誤無法繼續它的任務。 * 當它被告知停止當前任務。 前兩條我們已經知曉,可以通過算法實現。但如何取消當前任務?由于網絡效應,這最重要的一點是:如果你已經開始了一個goroutine,那么它很可能以某種有組織的方式與其他幾個goroutines合作。我們甚至可以把這種相互連接表現為一張圖表,這時該goroutine能否停下來還取決于處在交互的其他goroutines。我們將在下一章中繼續關注大規模并發產生的相互依賴關系,但現在讓我們考慮如何確保保證單個goroutine得到清理。 讓我們從一個簡單的goroutine泄漏開始: ``` doWork := func(strings <-chan string) <-chan interface{} { completed := make(chan interface{}) go func() { defer fmt.Println("doWork exited.") defer close(completed) for s := range strings { fmt.Println(s) } }() return completed } doWork(nil) // 這里還有其他任務執行 fmt.Println("Done.") ``` 我們看到doWork被傳遞了一個nil通道。所以strings通道永遠無法讀取到其承載的內容,而且包含doWork的goroutine將在這個過程的整個生命周期中保留在內存中(如果我們在doWork和主goutoutine中加入了goroutine,我們甚至會死鎖)。 在這個例子中,整個進程的生命周期很短,但是在一個真正的程序中,goroutines可以很容易地在一個長期生命的程序開始時啟動,導致內存利用率下降。 解決這種情況的方法是建立一個信號,按照慣例,這個信號通常是一個名為done的只讀通道。父例程將該通道傳遞給子例程,然后在想要取消子例程時關閉該通道。 這是一個例子: ``` doWork := func(done <-chan interface{}, strings <-chan string) <-chan interface{} { //1 terminated := make(chan interface{}) go func() { defer fmt.Println("doWork exited.") defer close(terminated) for { select { case s := <-strings: // Do something interesting fmt.Println(s) case <-done: //2 return } } }() return terminated } done := make(chan interface{}) terminated := doWork(done, nil) go func() { //3 // Cancel the operation after 1 second. time.Sleep(1 * time.Second) fmt.Println("Canceling doWork goroutine...") close(done) }() <-terminated //4 fmt.Println("Done.") ``` 1. 這里我們傳遞done通道給doWork函數。作為慣例,這個通道被作為首個參數。 2. 這里我們看到使用了for-select的使用模式之一。我們的目的是檢查done通道有沒有發出信號。如果有的話,我們退出當前goroutine。 3. 在這里我們創建另一個goroutine,一秒后就會取消doWork中產生的goroutine。 4. 這是我們在main goroutine中調用doWork函數返回結果的地方。 這會輸出: ``` Canceling doWork goroutine... doWork exited. Done. ``` 你可以看到盡管向doWork傳遞了nil給strings通道,我們的goroutine依然正常運行至結束。與之前的例子不同,本例中我們把兩個goroutine連接在一起之前,我們建立了第三個goroutine以取消doWork中的goroutine,并成功消除了泄漏問題。 前面的例子很好地處理了在通道上接收goroutine的情況,但是如果我們正在處理相反的情況:在嘗試向通道寫入值時阻塞goroutine會怎樣? ``` newRandStream := func() <-chan int { randStream := make(chan int) go func() { defer fmt.Println("newRandStream closure exited.") // 1 defer close(randStream) for { randStream <- rand.Int() } }() return randStream } randStream := newRandStream() fmt.Println("3 random ints:") for i := 1; i <= 3; i++ { fmt.Printf("%d: %d\n", i, <-randStream) } ``` 1. 當goroutine成功執行時我們打印一行消息。 這會輸出: ``` 3 random ints: 1: 5577006791947779410 2: 8674665223082153551 3: 6129484611666145821 ``` 你可以看到注釋1所在的打印語句并未執行。在循環的第三次迭代之后,我們的goroutine塊試圖將下一個隨機整數發送到不再被讀取的通道。我們無法告知它停下來,解決方案是為生產者提供一條通知它退出的通道: ``` newRandStream := func(done <-chan interface{}) <-chan int { randStream := make(chan int) go func() { defer fmt.Println("newRandStream closure exited.") defer close(randStream) for { select { case randStream <- rand.Int(): case <-done: return } } }() return randStream } done := make(chan interface{}) randStream := newRandStream(done) fmt.Println("3 random ints:") for i := 1; i <= 3; i++ { fmt.Printf("%d: %d\n", i, <-randStream) } close(done) //模擬正在進行的工作 time.Sleep(1 * time.Second) ``` 這會輸出: ``` 3 random ints: 1: 5577006791947779410 2: 8674665223082153551 3: 6129484611666145821 newRandStream closure exited. ``` 我們現在看到該goroutine被妥善清理。 現在我們知道如何確保goroutine不泄漏,我們可以制定一個約定:如果goroutine負責創建goroutine,它也負責確保它可以停止goroutine。 這個約定有助于確保程序在組合和擴展時可用。我們將在“管道”和“context包”中重新討論這種技術和規則。我們該如何確保goroutine能夠被停止根據goroutine的類型和用途而有所不同,但是它們 所有這些都是建立在傳遞done通道基礎上的。 * * * * * 學識淺薄,錯誤在所難免。我是長風,歡迎來Golang中國的群(211938256)就本書提出修改意見。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看