亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如果上下文被取消,則終止函數執行

如果上下文被取消,則終止函數執行

Go
qq_遁去的一_1 2022-06-06 15:33:23
我有這個最初不支持上下文的當前函數。func (s *Service) ChunkUpload(r *multipart.Reader) error {    chunk, err := s.parseChunk(r)    if err != nil {        return fmt.Errorf("failed parsing chunk %w", err)    }    if err := os.MkdirAll(chunk.UploadDir, 02750); err != nil {        return err    }    if err := s.saveChunk(chunk); err != nil {        return fmt.Errorf("failed saving chunk %w", err)    }    return nil}我已經更新了它的方法調用,現在將 acontext.Context作為它的第一個參數。我的主要目標是在上下文被取消后立即終止并返回函數。我最初的實現是這樣的。func (s *Service) ChunkUpload(ctx context.Context, r *multipart.Reader) error {    errCh := make(chan error)    go func() {        chunk, err := s.parseChunk(r)        if err != nil {            errCh <- fmt.Errorf("failed parsing chunk %w", err)            return        }        if err := os.MkdirAll(chunk.UploadDir, 02750); err != nil {            errCh <- err            return        }        if err := s.saveChunk(chunk); err != nil {            errCh <- fmt.Errorf("failed saving chunk %w", err)            return        }    }()    select {    case err := <-errCh:        return err    case <-ctx.Done():        return ctx.Err()    }}然而,當我想到代碼的執行時,我意識到這并沒有達到我的目標。由于所有函數的邏輯都在一個單獨的 go 例程中,即使上下文被取消并且我ChunkUpload提前返回,go 例程中的代碼將繼續執行,因此與原始代碼沒有真正的區別。下一個雖然可以,只需將上下文傳遞給所有內部函數,例如s.parseChunk,s.saveChunk但是這個選項似乎也不正確,因為我需要在每個函數中實現取消。將這個原始函數重構為上下文感知并在上下文被取消后立即終止的正確方法是什么?
查看完整描述

2 回答

?
斯蒂芬大帝

TA貢獻1827條經驗 獲得超8個贊

函數調用和 goroutines 不能被調用者終止,函數和 goroutines 必須支持取消,通常通過context.Context值或done通道。


在任何一種情況下,函數都負責檢查/監視上下文,如果請求取消(當上下文的完成通道關閉時),則提前返回。沒有更簡單/自動的方法。


如果任務在循環中執行代碼,一個方便的解決方案是在每次迭代中檢查完成的通道,如果它關閉則返回。如果任務是一個“整體”,則實現者負責使用/插入“檢查點”,如果請求取消,則可以合理地提前中止任務。


檢查 done 通道是否關閉的一種簡單方法是使用 non-blocking select,例如:


select {

case <-ctx.Done():

    // Abort / return early

    return

default:

}

當任務使用其他通道操作時必須小心,因為它們可能會以不確定的方式阻塞。這些選擇也應該包括ctx.Done()頻道:


select {

case v := <- someChannel:

    // Do something with v

case <-ctx.Done():

    // Abort / return early

    return

}

還要小心,因為如果上面的 receive from someChannelnever 阻塞,則不能保證正確處理取消,因為如果多個通信可以在 a 中進行select,則隨機選擇一個(并且不能保證<-ctx.Done()永遠選擇)。在這種情況下,您可以結合上述 2:首先對取消進行非阻塞檢查,然后將 aselect與您的通道操作和取消監控一起使用。


查看完整回答
反對 回復 2022-06-06
?
溫溫醬

TA貢獻1752條經驗 獲得超4個贊

當我們談到取消時,我們談到了一個長期運行的函數或一個重復多次的塊,例如http.Serve()


至于您的情況,假設saveChunk運行將花費幾秒鐘,并且您想在保存時取消。所以我們可以把塊分成幾塊,在每一塊之后一個接一個地保存。


for i:=0;i<n;i++{

    select {

        case err := <- s.saveChunk(chunk[i]):

        {

             if err != nil {

                  fmt.Errorf("failed saving chunk %w", err)

                  return

              }

        }

        case <-ctx.Done():

              return

    }

}


查看完整回答
反對 回復 2022-06-06
  • 2 回答
  • 0 關注
  • 133 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號