我正在使用gRPC开发.net core 2.0应用程序并找出问题:在删除对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();
}
}
如果您运行示例,您将看到此应用程序使用的10%(在我的PC上)。即使在 list = null 和 GC.Collect()
之后我认为 ClientBase 的原因是不要调用 Channel.ShutdownAsync()。
所以问题是:
解决问题的更好方法是什么?
P.S。实际上我使用“由协议缓冲编译器生成”客户端
Client: Grpc.Core.ClientBase<TDto>
我无法在生成的类中明确更改终结器
答案 0 :(得分: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();
}
}