C# Grpc通道在关机时未完全关闭异步

C# Grpc通道在关机时未完全关闭异步,c#,grpc,C#,Grpc,我想在grpc呼叫可能正在进行时完全关闭我的表单应用程序。我认为ShutdowAsync将关闭任何正在进行的调用,但似乎我没有将其放在正确的位置,或者它不负责关闭通道。当应用程序关闭时,使用CompleteAsync()来完成全面、结束所有流的最简单方法是什么 还有一个额外的困难是,我正在使用另一个类来保存所有grpc内容,我不确定在什么时候会收集垃圾。当然,我可以做一个取消令牌,让它在我关闭时取消所有正在进行的呼叫,但是,我应该在哪里做取消?在模型的析构函数或视图的析构函数中 示例代码: us

我想在grpc呼叫可能正在进行时完全关闭我的表单应用程序。我认为ShutdowAsync将关闭任何正在进行的调用,但似乎我没有将其放在正确的位置,或者它不负责关闭通道。当应用程序关闭时,使用
CompleteAsync()
来完成全面、结束所有流的最简单方法是什么

还有一个额外的困难是,我正在使用另一个类来保存所有grpc内容,我不确定在什么时候会收集垃圾。当然,我可以做一个取消令牌,让它在我关闭时取消所有正在进行的呼叫,但是,我应该在哪里做取消?在模型的析构函数或视图的析构函数中

示例代码:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using Grpc.Core;
using OdinIntegrationSyncRPC;

namespace MinViaExample
{
    public partial class Form1 : Form
    {
        ExampleModel ex;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ex = new ExampleModel();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            ex.Sync();
        }

        private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            await ex.ShutdownChannelAsync();
        }
    }

    public class ExampleModel
    {
        Channel channel;
        OdinIntegrationSync.OdinIntegrationSyncClient client;

        private AsyncDuplexStreamingCall<LSHeartbeat, LastStudentsPacket> CurrentCall;

        public ExampleModel()
        {
            channel = new Channel("127.0.0.1:50052", ChannelCredentials.Insecure);
            client = new OdinIntegrationSync.OdinIntegrationSyncClient(channel);
        }

        public async void Sync()
        {
            var CTokenSource = new CancellationTokenSource();
            CancellationToken CToken = CTokenSource.Token;

            using (CurrentCall = client.GetLastStudentSync())
            {
                await CurrentCall.RequestStream.WriteAsync(new LSHeartbeat() { ContinueRequest = true });

                while (await CurrentCall.ResponseStream.MoveNext(CancellationToken.None) && !CToken.IsCancellationRequested)
                {
                    LastStudentsPacket lsp = CurrentCall.ResponseStream.Current;
                    foreach (OStudent o in lsp.Students)
                    {
                        // blahblah
                    }
                    await CurrentCall.RequestStream.WriteAsync(new LSHeartbeat() { ContinueRequest = true });
                }
            }
            await CurrentCall.RequestStream.CompleteAsync();
        }

        public async Task ShutdownChannelAsync()
        {
            await channel.ShutdownAsync();
        }
    }
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows.Forms;
使用Grpc.Core;
使用OdinIntegrationSyncRPC;
名称空间示例
{
公共部分类Form1:Form
{
例如模型;
公共表格1()
{
初始化组件();
}
私有void Form1\u加载(对象发送方、事件参数e)
{
ex=新的ExampleModel();
}
显示私有void Form1_(对象发送方,事件参数e)
{
例如Sync();
}
私有异步无效Form1\u FormClosing(对象发送方,FormClosingEventArgs e)
{
等待例如ShutdownChannelAsync();
}
}
公共类示例模型
{
渠道;
OdinIntegrationSync.OdinIntegrationSyncClient客户端;
专用AsyncDuplexStreamingCall CurrentCall;
公共示例模型()
{
通道=新通道(“127.0.0.1:50052”,通道凭证。不安全);
client=new-OdinIntegrationSync.odinintegrationsynclient(通道);
}
公共异步void Sync()
{
var CTokenSource=new CancellationTokenSource();
CancellationToken CToken=CTokenSource.Token;
使用(CurrentCall=client.GetLastStudentSync())
{
等待CurrentCall.RequestStream.WriteAsync(新的LSHeartbeat(){ContinueRequest=true});
while(等待CurrentCall.ResponseStream.MoveNext(CancellationToken.None)和&!CToken.IsCancellationRequested)
{
LastStudentsPacket lsp=CurrentCall.ResponseStream.Current;
foreach(lsp学生中的大多数学生)
{
//布拉布拉赫
}
等待CurrentCall.RequestStream.WriteAsync(新的LSHeartbeat(){ContinueRequest=true});
}
}
等待CurrentCall.RequestStream.CompleteAsync();
}
公共异步任务关闭ChannelAsync()
{
等待channel.shutdownsync();
}
}
}
编辑:以上代码已从初始问题(添加了ShutdownChannel调用到FormClosing)更改。现在只有服务器崩溃,出现
System.InvalidOperationException:“已完成”。
。客户机似乎干净地退出了


如果我错过了一些基本的东西,请道歉。

那么你看到了什么?老实说,我不会依赖于终结器——这会使事情变得非常不确定。我会亲自尝试在UI端设置一个取消令牌,并允许在您想退出时取消该令牌。@JonSkeet如果有人点击X,我什么时候取消该令牌?在FormClosing中?是的,这似乎是合理的。@JonSkeet我这样问是因为这似乎不起作用:)我应该只调用
call.CompleteAsync
,还是只调用
Channel.shutdownSync
,或者两者都调用?如果没有比“似乎不起作用”更详细的信息,很难帮助你诚实地说。我们不知道应用程序是否没有关闭,是否抛出了异常,或者是否完全是其他原因。(您在文章的底部报告了一个异常,但没有完整的堆栈跟踪或任何类似的内容。)