2017-03-05 11 views
4

再帰関数を実行する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は機能せず、たぶん再帰のインスタンスが1つだけ終了します。

終了信号を送信して再帰関数のすべてのインスタンスを停止するにはどうすればよいですか?

+0

ゴーでは、我々はcontext.Contextを持っています依存するゴルーチンの階層を作成する。 http://stackoverflow.com/questions/42516717/how-to-stop-goroutine/42518866#42518866 –

+0

なぜチャンネルを閉じたくないのですか?それはあなたが何を記述するのが最も簡単な方法なので、他にどのような制限があるか知ることは良いことです。 – djd

+0

1つのチャンネルを使用している場合は、アプリ内のすべてのゴルーチンを停止しています。または、この特定の目的にチャネルを使用する場合は、それらをすべて手動で管理する必要があります。また、あなたのアプリでアクティブなgorutinesの一部を停止する必要がある場合は、どうすればよいでしょうか?さらに、 'context.Context'を使うことは、Goのゴルーチンを管理するための慣習的なパターンです。 –

答えて

4

あなたはcontextを使って何をしようとしていますか?

あなたがのDone()チャンネルを引き起こすであろう、あなたは外部から停止する必要がある関数の最初のパラメータとしてcontext.Contextオブジェクトを渡して、関数に「キャンセル信号」を送信するために、対応するcancel関数を呼び出すことができます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

まず、 '<-ctx.Done()'のチェックは、 'RecursiveFunc'関数の先頭だけでなく' for'ループの内部でも行う必要があります。また、 'main'関数では、ゴルーチンをランチングする前に' wg.Add(1) 'を呼び出さなければならないので、細工された遅延(' time.Sleep(10 * time.Millisecond) ')の必要はありません。また、 'RecursiveFunc'が10ms未満でも、あなたのgoroutineは決してランチされず、' wg.Wait() 'はすぐに戻ります。 –

+0

@KavehShahbazian:あなたの2番目の点に同意しますが、 'for'内の' RecursiveFunc'を 'ctx.Done'の後に実行して終了させるのは間違っていますか? – deepmax

+0

文脈がその時点でキャンセルされたとしても、それはちょうど新しいゴルーチンをランチし続けます。 –

-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

関連する問題