2017-12-01 2 views
2

gRPCを使って.net core 2.0アプリケーションを開発していて、問題を見つけました。私のgRPCクライアントクラスのインスタンスへの参照を削除した後も、resourses(メモリとプロセッサ)を使用するチャンネルがあります。 例コード:gRPCクライアントはチャンネルを処分しません

public class MyClient : ClientBase 
    { 
     public MyClient(Channel channel) : base(channel) 
     { 
     } 
    } 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      var list = new List<MyClient>(); 
      for (var i = 0; i < 10000; i++) 
      { 
       Console.WriteLine($"Creating {i} instance"); 
       list.Add(new MyClient(new Channel("127.0.0.1:61783", ChannelCredentials.Insecure))); 
      } 

      Console.WriteLine("press enter to list = null"); 
      Console.ReadLine(); 
      list = null; 

      Console.WriteLine("press enter to GC.Collect();"); 
      Console.ReadLine(); 
      GC.Collect(); 

      Console.WriteLine("press enter to exit"); 
      Console.ReadLine(); 
     } 
    } 

uは例を実行する場合は、(私のPC上の)10%は、このアプリケーションで使用されることをu'll参照してください。でもリスト= nullをGC.Collect()の後に

私が思う理由は、Channel.ShutdownAsync()を呼び出さないClientBaseです。

そこで質問です:

問題を解決するためのより良い方法は何ですか?

p.s.実際に私は、クライアント

Client: Grpc.Core.ClientBase<TDto> 

「プロトコルバッファコンパイラによって生成された」使用して、私は明示的に生成されたクラスにファイナライザを変更することはできません

+0

自分でチャンネルをシャットダウンするのはなぜですか? – Evk

+0

私はそれが最良の解決策であるかどうかはわかりません:{}やfinilize()メソッドを使ってリソースを処分するのがベストプラクティスだと思います... –

+4

あなたはサードパーティのライブラリを使っています。 'IDisposable'、偶数チャンネルを実装しているものはありません)、この場合、最良の方法はチャンネルを終了し、ガベージコレクタのようなものに依存しないでチャンネルを明示的にシャットダウンすることです。さて、とにかくあなた自身を解放できるいくつかのリソースをリリースするためにGCに頼らないのは常にベストです。 – Evk

答えて

0

可能性のある提案は、クライアントがIDisposableを実装し、作ることであろうDispose方法でChannel.ShutdownAsync()に電話してください。

public class MyClass : Client, IDisposable { 
    Channel channel; 
    private bool _isDisposed = false; 
    private readonly object _lock = new object(); 

    public MyClass(Channel channel) 
     : base(channel) { 
     this.channel = channel; 
     this.channelDisposing += onDisposing; 
    } 

    public Channel Channel { get { return channel; } } 

    private event EventHandler channelDisposing = delegate { }; 

    async void onDisposing(object sender, EventArgs e) { 
     await channel.ShutdownAsync(); 
     channel = null; 
    } 

    public void Dispose() { 
     if (!_isDisposed) { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 
    } 

    void Dispose(bool disposing) { 
     // No exception should ever be thrown except in critical scenarios. 
     // Unhandled exceptions during finalization will tear down the process. 
     if (!_isDisposed) { 
      try { 
       if (disposing) { 
        // Acquire a lock on the object while disposing. 
        if (channel != null) { 
         lock (_lock) { 
          if (channel != null) { 
           channelDisposing(this, EventArgs.Empty); 
          } 
         } 
        } 
       } 
      } finally { 
       // Ensure that the flag is set 
       _isDisposed = true; 
      } 
     } 
    } 
} 

これは、あなたが今、クライアント上Disposeを呼び出してリソースを解放するか、それがスコープの外に出たとき、それはあなたのために行われるようにusingでそれらをラップすることができます。

public class Program { 
    public static void Main(string[] args) { 
     var list = new List<MyClient>(); 
     for (var i = 0; i < 10000; i++) { 
      Console.WriteLine($"Creating {i} instance"); 
      list.Add(new MyClient(new Channel("127.0.0.1:61783", ChannelCredentials.Insecure))); 
     } 

     Console.WriteLine("press enter to dispose clients"); 
     Console.ReadLine(); 
     list.ForEach(c => c.Dispose()); 

     Console.WriteLine("press enter to list = null"); 
     Console.ReadLine(); 
     list = null; 

     Console.WriteLine("press enter to GC.Collect();"); 
     Console.ReadLine(); 
     GC.Collect(); 

     Console.WriteLine("press enter to exit"); 
     Console.ReadLine(); 
    } 
} 
+0

クラスMyClientがGrpcによって自動的に生成されるように変更することはできません(私のポストを参照)。 Grpcジェネレータを使用してそれを変更する方法が分かっている場合のみ。 –

+0

@TimurLemeshko次に、生成されたクラスが封印されていない場合は、 'MyClass'を継承する別のクラスを作成し、' IDisposable'を実装します。私の前提は、あなたが「MyClass」コードを管理していたということでした。 – Nkosi

+3

私はこのやり方が本当に好きではありません。 dispose( 'GetAwaiter()。GetResult()'でさえ)で 'Task'を返す呼び出しメソッドは正しく見えません。呼び出し側が 'ShutdownAsync'に直接アクセスできるようにすると、' Task.WhenAll() 'のようなことができ、一般的にはすでに非同期コードベースに統合されています。 – Evk

関連する問題