2017-03-05 85 views
4

凝視運行遞歸函數的goroutine,我想發送一個信號來停止這些遞歸函數。這是函數(功能並不重要):停止在goroutine中的所有遞歸函數

func RecursiveFunc(x int, depth int, quit chan bool) int { 

    if depth == 0 { 
     return 1 
    } 

    if quit != nil { 
     select { 
     case <-quit: 
      return 0 
     default: 
     } 
    } 

    total := 0 

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

     y := RecursiveFunc(x, depth - 1, quit) 

     if y > 0 { 
      total += y 
     } 

    } 

    return total 
} 

該函數可以採用做很長一段時間,我想發送的退出信號,之後停止和使用的結果(不管它是什麼)。要運行它:

import (
    "fmt" 
    "time" 
    "sync" 
) 

func main() { 

    quit := make(chan bool) 
    wg := &sync.WaitGroup{} 
    result := -1 

    go func() { 
     defer wg.Done() 
     wg.Add(1) 
     result = RecursiveFunc(5, 20, quit) 
    }() 

    time.Sleep(10 * time.Millisecond) 

    close(quit) // Using `quit <- true` doesn't work 

    wg.Wait() 

    fmt.Println(result) 
} 

要停止夠程,我使用的信道說quit和關閉它後,程序效果很好,但我不希望真的關閉通道,我想只是發信號quit <- true。但是,quit <- true不起作用,我可能只退出遞歸的一個實例。

如何通過發送退出信號來停止遞歸函數的所有實例?

+0

在轉到我們context.Context創建依賴門廊的層次結構;請參閱http://stackoverflow.com/questions/42516717/how-to-stop-goroutine/42518866#42518866 –

+0

爲什麼你不想關閉頻道?這是你描述的最簡單的方法,所以最好知道你有什麼其他限制。 – djd

+0

如果您使用單個通道,則會停止應用中的所有goroutines。或者,如果您希望將渠道用於此特定用途,則必須手動管理它們。如果您只需要在應用程序中停止一部分活動goroutines,那麼您會怎麼做?此外,使用'context.Context'是Go中管理goroutines的慣用模式。 –

回答

4

你可以使用context來做你想要做的事情。

您可以傳遞一個context.Context對象作爲第一個參數,你需要從外部停止功能,並調用相應的cancel函數來發送一個「消除信號」的功能,這將導致的對Done()通道context.Context將被關閉,並且因此被叫功能將在select聲明中被通知取消信號。

下面是函數如何處理使用context.Context消除信號:

func RecursiveFunc(ctx context.Context, x int, depth int) int { 

    if depth == 0 { 
     return 1 
    } 

    select { 
    case <-ctx.Done(): 
     return 0 
    default: 
    } 

    total := 0 

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

     y := RecursiveFunc(ctx, x, depth-1) 

     if y > 0 { 
      total += y 
     } 

    } 

    return total 
} 

這裏是你如何調用該函數與新簽名:

func main() { 

    wg := &sync.WaitGroup{} 
    result := -1 

    ctx, cancel := context.WithCancel(context.Background()) 

    go func() { 
     defer wg.Done() 
     wg.Add(1) 
     result = RecursiveFunc(ctx, 5, 20) 
    }() 

    time.Sleep(10 * time.Millisecond) 

    cancel() 

    wg.Wait() 

    fmt.Println(result) 
} 
+0

首先,您不僅要在'RecursiveFunc'函數的開頭檢查'<-ctx.Done()',還要在'for'循環內檢查'<-ctx.Done()。第二,在你的'main'函數中,你必須在執行goroutine之前調用'wg.Add(1)',這樣就不需要精心設計的延遲('time.Sleep(10 * time.Millisecond)')和同樣,如果'RecursiveFunc'少於10ms,你的goroutine將永遠不會被激活,'wg.Wait()'會立即返回。 –

+0

@KavehShahbazian:我同意你的第二點,但是在'for'裏面讓'RecursiveFunc'執行並在'ctx.Done'之後退出有什麼不對? – deepmax

+0

即使上下文在那個時候被取消,它也只是不停地發送新的goroutine,而不應該這樣做。 –

-1

嘗試添加標誌以繼續執行,但它可能不是線程安全的。

var finishIt bool 

func RecursiveFunc(x int, depth int, quit chan bool) int { 
    if finishIt { 
    return 0 
    } 
//other code here 
} 


//some code here, but than we decide to stop it 
finishIt = true 
+0

我認爲這是可以的變量是全球性的,而不是出口這個特定的模塊。 – vodolaz095