c# - 在 SignalR Core 断开事件中等待几秒钟,而不阻止存在的应用程序

标签 c# asp.net-core signalr asp.net-core-2.1

在我的集线器的 OnDisconnectedAsync 事件中,我想在执行某些操作之前等待几秒钟。我尝试使其异步使用 non-blocking Task.Delay :

    public override async Task OnDisconnectedAsync(Exception exception) {
        var session = (VBLightSession)Context.Items["Session"];
        activeUsers.Remove(session.User.Id);

        await Task.Delay(5000);
        if(!activeUsers.Any(u => u.Key == session.User.Id)) {
            await Clients.All.SendAsync("UserOffline", UserOnlineStateDto(session));
        }

        await base.OnDisconnectedAsync(exception);
    }

虽然这按预期工作,但我注意到我无法立即关闭控制台应用程序。看起来它等待 5 秒延迟才能完成。我该如何解决这个问题,退出应用程序也只是退出这些延迟?

我看到的唯一替代方案是 Creating a classic thread and inject IHubContext ,但这似乎无法很好地扩展,并且对于这个简单的任务来说有点矫枉过正。

背景

我有一个在线用户列表。当用户浏览多页面应用程序时,他们会在新的 HTTP 请求期间短暂断开连接。为了避免在线列表中出现这种闪烁(用户离线并再次直接在线),我想从用户列表中删除断开连接的用户,但不立即通知 WS 客户端。

相反,我想等 5 秒钟。仅当列表中仍然缺少客户端时,我才知道客户端尚未重新连接,并通知其他用户。为此,我需要在断开连接事件上休眠。除了应用程序退出时的延迟(这在开发过程中很烦人)之外,上述解决方案效果很好。

不应该使用像 Angular 或其他框架这样的单页面应用程序,原因有几个,主要是性能和 SEO。

最佳答案

我了解了 CancellationToken ,它可以传递给Task.Wait。它可用于中止任务。使用 CancellationTokenSource 创建这样的 token 似乎可以很好地以编程方式取消 token (例如在某些条件下)。

但我发现the ApplicationStopping token in the IApplicationLifetime interface ,当应用程序关闭时请求取消。所以我可以简单地注入(inject)

namespace MyApp.Hubs {
    public class MyHub : Hub {
        readonly IApplicationLifetime appLifetime;
        static Dictionary<int, VBLightSession> activeUsers = new Dictionary<int, VBLightSession>();
        public MyHub(IApplicationLifetime appLifetime) {
            this.appLifetime = appLifetime;
        }
    }
}

只有在没有从此 token 请求取消时才休眠

public override async Task OnDisconnectedAsync(Exception exception) {
    var session = (VBLightSession)Context.Items["Session"];
    activeUsers.Remove(session.User.Id);
    // Prevents our application waiting to the delay if it's closed (especially during development this avoids additionally waiting time, since the clients disconnects there)
    if (!appLifetime.ApplicationStopping.IsCancellationRequested) {
        // Avoids flickering when the user switches to another page, that would cause a directly re-connect after he has disconnected. If he's still away after 5s, he closed the tab
        await Task.Delay(5000);
        if (!activeUsers.Any(u => u.Key == session.User.Id)) {
            await Clients.All.SendAsync("UserOffline", UserOnlineStateDto(session));
        }
    }
    await base.OnDisconnectedAsync(exception);
}

这是有效的,因为当关闭应用程序时,SignalR 会将其检测为断开连接(尽管它是由服务器引起的)。所以他在退出前等待了 5000 秒,就像我在问题中假设的那样。但使用 token 后,IsCancellationRequested 设置为 true,因此在这种情况下无需额外等待。

关于c# - 在 SignalR Core 断开事件中等待几秒钟,而不阻止存在的应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57828434/

相关文章:

c# - 在 ASP.NET Core 3.1 中创建 session

c# - 什么是服务以及为什么将它们添加到 ASP.NET Core 中?

android - 如何在 Android Studio 中设置 SignalR 以忽略 SSL 问题以进行开发

c# - 在 C# 中验证文件夹名称

c# - Blazor - 隐藏特定页面的侧边栏和顶部栏(不适用于整个网站)

c# - 我将 'System.Web.UI.WebControls.TextBox' 放入 sql 表中,而不是在文本框中输入实际数据

javascript - 如何防止从控制台对 SignalR 聊天进行 jQuery 调用?

c# - 无法加载文件或程序集 system.memory 版本 4.1.0.0

c# - 如何在 Windows Server 2008 的 IE8 中启用 ActiveX?

c# - HasMorePages 不工作。设置该位会导致在同一页上打印所有项目