3 回答

TA貢獻(xiàn)1854條經(jīng)驗(yàn) 獲得超8個(gè)贊
取消(阻塞)發(fā)送
您最初的問(wèn)題詢問(wèn)如何取消發(fā)送操作。頻道上的發(fā)送基本上是“即時(shí)的”。如果通道的緩沖區(qū)已滿并且沒(méi)有準(zhǔn)備好的接收器,則通道上的發(fā)送會(huì)阻塞。
您可以使用select聲明和cancel您關(guān)閉的頻道“取消”此發(fā)送,例如:
cancel := make(chan struct{})
select {
case ch <- value:
case <- cancel:
}
在另一個(gè) goroutine 上關(guān)閉cancel通道close(cancel)將使上面的選擇放棄發(fā)送ch(如果它正在阻塞)。
但如前所述,發(fā)送在“就緒”通道上是“即時(shí)的”,并且發(fā)送首先評(píng)估要發(fā)送的值:
results <- w.Check()
這首先必須運(yùn)行w.Check(),一旦完成,它的返回值將在 上發(fā)送results。
取消函數(shù)調(diào)用
所以你真正需要的是取消w.Check()方法調(diào)用。為此,慣用的方法是傳遞一個(gè)context.Context可以取消的值,它w.Check()本身必須監(jiān)視并“服從”這個(gè)取消請(qǐng)求。
請(qǐng)參見(jiàn)在取消上下文時(shí)終止函數(shù)執(zhí)行
請(qǐng)注意,您的函數(shù)必須明確支持這一點(diǎn)。沒(méi)有函數(shù)調(diào)用或 goroutines 的隱式終止,請(qǐng)參閱取消 Go 中的阻塞操作。
所以你Check()應(yīng)該看起來(lái)像這樣:
// This Check could be quite time-consuming
func (w *Work) Check(ctx context.Context, workDuration time.Duration) bool {
// Do your thing and monitor the context!
select {
case <-ctx.Done():
return false
case <-time.After(workDuration): // Simulate work
return true
case <-time.After(2500 * time.Millisecond): // Simulate failure after 2.5 sec
return false
}
}
CheckAll()可能看起來(lái)像這樣:
func CheckAll(works []*Work) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
num := len(works)
results := make(chan bool, num)
wg := &sync.WaitGroup{}
for i, w := range works {
workDuration := time.Second * time.Duration(i)
wg.Add(1)
go func(w *Work) {
defer wg.Done()
result := w.Check(ctx, workDuration)
// You may check and return if context is cancelled
// so result is surely not sent, I omitted it here.
select {
case results <- result:
case <-ctx.Done():
return
}
}(w)
}
go func() {
wg.Wait()
close(results) // This allows the for range over results to terminate
}()
for result := range results {
fmt.Println("Result:", result)
if !result {
cancel()
break
}
}
}
測(cè)試它:
CheckAll(make([]*Work, 10))
輸出(在Go Playground上嘗試):
Result: true
Result: true
Result: true
Result: false
我們true打印了 3 次(工作在 2.5 秒內(nèi)完成),然后故障模擬開(kāi)始,返回false并終止所有其他工作。
請(qǐng)注意,sync.WaitGroup上面示例中的 并不是嚴(yán)格需要的,因?yàn)閞esults它有一個(gè)能夠保存所有結(jié)果的緩沖區(qū),但總的來(lái)說(shuō)它仍然是一個(gè)很好的做法(如果您將來(lái)使用較小的緩沖區(qū))。

TA貢獻(xiàn)1772條經(jīng)驗(yàn) 獲得超8個(gè)贊
簡(jiǎn)短的回答是:不。
return
除非 goroutine 本身到達(dá)其堆棧的末尾,否則您不能取消或關(guān)閉任何 goroutine 。
如果你想取消某些東西,最好的方法是將 a 傳遞給它們并在例程中context.Context
收聽(tīng)它。context.Done()
每當(dāng)上下文被取消時(shí),你應(yīng)該return
在執(zhí)行 defers(如果有的話)后 goroutine 會(huì)自動(dòng)死掉。

TA貢獻(xiàn)1825條經(jīng)驗(yàn) 獲得超4個(gè)贊
package main
import "fmt"
type Work struct {
// ...
Name string
IsSuccess chan bool
}
// This Check could be quite time-consuming
func (w *Work) Check() {
// return succeed or not
//...
if len(w.Name) > 0 {
w.IsSuccess <- true
}else{
w.IsSuccess <- false
}
}
//堆排序
func main() {
works := make([]*Work,3)
works[0] = &Work{
Name: "",
IsSuccess: make(chan bool),
}
works[1] = &Work{
Name: "111",
IsSuccess: make(chan bool),
}
works[2] =&Work{
Name: "",
IsSuccess: make(chan bool),
}
for _,w := range works {
go w.Check()
}
for i,w := range works{
select {
case checkResult := <-w.IsSuccess :
fmt.Printf("index %d checkresult %t \n",i,checkResult)
}
}
}
- 3 回答
- 0 關(guān)注
- 135 瀏覽
添加回答
舉報(bào)