2017-09-07 6 views
1
package main 

import (
    "sync" 
    "runtime" 
) 

type S struct { 
    chs chan int 
} 

var wg sync.WaitGroup 

func worker(s *S) { 
    for i := range s.chs { 
     println("In worker, ch = ", i) 
    } 

    wg.Done() 
} 

func main() { 
    s := S{make(chan int)} 

    runtime.SetFinalizer(&s, func(ss *S) { 
     println("Finalizer") 
     close(ss.chs) 
    }) 


    wg.Add(1) 

    go worker(&s) 
    for i := 0; i < 1; i++ { 
     s.chs <- 1 
    } 

    runtime.GC() 

    wg.Wait() 
} 

出力は(1.8.3を行く) 'の範囲' とGolangの 'チャンネル' に関連したガベージコレクション:バグまたは機能?

労働者で

、CH = 1人の

ファイナ


私はこのプログラムがデッドロックすることを期待しています。 runtime.GC()は、worker()s.chsへの参照を保持しているので、sを収集しません。

ただし、1.8.3で終了します。 sのファイナライザでは、close(s.chs)でも正常に呼び出されます。

rangeとGCで特別なことがあるのだろうかと思います。

ありがとうございました。

+0

このようにrangeキーワードは特別なものではなく、コンパイラの最適化によって 's'が再び使用されないと判断できるということだけです。 's'をグローバルにして、デッドロックします。 – JimB

+0

単に実験するのではなく、代わりに「デストラクタ」を実装しようとしている場合に備えて、私はあなたに警告しなければなりません。 Idiomatic Goのカスタム型は、FDやソケットをラップする型に対して 'Close()'のような明示的な "リソース解放/解放"メソッドを提供する必要があります。 [関連項目](https://stackoverflow.com/q/32768243/720999) – kostix

答えて

1

私は、これは何が起こっているかである場合には100%わからないんだけど、runtime godocから、二SetFinalizerのための最後の段落に:

例えば、Aが含ま構造体への場合は、Pポイントファイルディスクリプタ d、pにはファイルディスクリプタをクローズするファイナライザがあり、関数内で最後にpを使用した場合は、syscall.Write(pd、buf、 サイズ)を呼び出すと、すぐにpに到達できない可能性がありますプログラムが syscall.Writeと入力すると、クローズファイル 記述子(または、悪いことに、別のゴルーチンによって をオープンしたまったく異なるファイル記述子)に書き込みを行っているため、syscall.Writeが失敗する原因となります。この問題を回避するには、syscall.Writeを呼び出した後に runtime.KeepAlive(p)を呼び出します。 Finalizerはおそらくのように、すぐに問題のvarの最後の使用後には早く実行できることを私に示唆

私はこの可能性については考えていませんでしたが、技術的には、GCは変数のスコープがなくなる前に変数を収集できるかどうかを知ることができます。この単純な例を考えてみましょう。

strをGCで収集できなかった理由はありません。それは決して再び使われることはなく、それはこれを知っている可能性が非常に高いです。

+2

はい、SSAコンパイラーのバックエンドは、変数が使用されなくなった時点を判断するのがはるかに簡単であるため、変数を使用した直後に変数が「範囲外になる」ようにします。私はこの例がgo1.6のような古いコンパイラでは失敗することを保証します。 – JimB

+0

@JimBはい、この例はgo 1.7で失敗します。 – Bef0rewind

関連する問題