2017-04-16 18 views
1

我需要調用一個以Context作爲參數的函數。該代碼塊可以訪問用於通知應該取消操作的通道。是否有更簡潔的方式來創建在頻道上接收後取消的上下文?

這是我目前使用的是什麼取消Context當接收到一個值:

func doSomething(stop <-chan bool) { 
    ctx, cancel := context.WithCancel(context.Background()) 
    go func() { 
     select { 
     case <-ctx.Done(): 
     case <-stop: 
      cancel() 
     } 
    }() 
    longRunningFunction(ctx) 
} 

預期的控制流程如下:

  • 如果任務運行完成,它將取消上下文,<-ctx.Done()將會觸發,並且該goroutine將終止。

  • 如果在stop上收到一個值,上下文將被取消,通知任務它應該退出。這種情況再次發生時,會議程序將終止。

這似乎過於複雜。有沒有更簡單的方法來完成預期的行爲?

+1

如果你的實際代碼工作正常,這可能是一個更好的擬合的https://codereview.stackexchange.com/ – oliverpool

+0

@oliverpool我做了,它被及時關閉,他們建議我在這裏問。 –

+1

我認爲你需要在'doSomething'中推遲cancel()',否則當'longRunningFunction'完成時不會被'stop'通道取消而泄漏gorutine。你可能還應該使用'context.TODO()'而不是'context.Background()',因爲從長遠來看,我們希望用上下文替換stop cannel。 – ain

回答

1

由於@ain mentionned,你的代碼目前泄漏如果longRunningFunction運行到最後並沒有什麼上stop發送(或不會被關閉)的夠程:在select語句將永遠不會實現(爲context的唯一途徑要做的事情是當stop有什麼東西來電話cancel)。

這裏是一個辦法解決它(主要是@ain's comment實現):

func doSomething(stop <-chan bool) { 
    ctx := context.TODO() // because in the future, you might pass a ctx arg to this function, from which you could then "inherit" 
    ctx, cancel := context.WithCancel(ctx) 
    defer cancel() // to be sure to release the associated resources whatever happens (and prevent the following goroutine from leaking) 
    go func() { 
     select { 
     case <-ctx.Done(): 
     case <-stop: 
      cancel() 
     } 
    }() 
    longRunningFunction(ctx) 
} 
相關問題