c# - 在 SignalR Core 断开事件中等待几秒钟而不阻塞应用程序存在
问题描述
如果OnDisconnectedAsync
是我的集线器,我想在执行某些操作之前等待几秒钟。我试图使其异步以使用非阻塞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 秒延迟完成。我该如何解决这个问题,退出应用程序也只是退出这些延迟?
我看到的唯一选择是创建一个经典线程并注入IHubContext
,但这似乎不能很好地扩展,并且对于这个简单的任务来说有点矫枉过正。
背景
我有一个在线用户列表。当用户在多页应用程序中导航时,他们在新的 HTTP 请求期间会在短时间内断开连接。为了避免在线列表中出现这种闪烁(用户下线并再次直接在线),我想从用户列表中删除断开连接的用户,但不立即通知 WS 客户端。
相反,我想等待 5 秒钟。仅当列表中仍然缺少客户端时,我才知道客户端尚未重新连接并通知其他用户。为此,我需要在断开连接事件上睡觉。上述解决方案效果很好,除了应用程序退出延迟(这在开发过程中很烦人)。
不应该使用像 Angular 或其他框架这样的单页应用程序,主要是性能和 SEO。
解决方案
我了解了CancellationToken,它可以传递给Task.Wait
. 它可以用来中止任务。使用创建这样的令牌CancellationTokenSource
似乎可以很好地以编程方式取消令牌(例如在某些情况下)。
但是我在界面中找到了 ApplicationStopping 令牌IApplicationLifetime
,它在应用程序关闭时请求取消。所以我可以简单地注入
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;
}
}
}
并且仅在没有从此令牌请求取消时才休眠
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,所以在这种情况下没有额外的等待。
推荐阅读
- python - 在 python 中使用 pyodbc/turbodc 从现有表创建 SQL 表
- swift3 - 使用 Xcode 版本 11.3.1 将 swift 3 代码迁移到 swift 5
- javascript - 这个java正则表达式是做什么的?
- php - Guzzle/Curl 发送两次时出现奇怪的错误
- keras - 如何在具有不同时间步长的多个时间序列数据上训练 LSTM 模型?
- java - IntelliJ IDEA 不允许我单击 jar 并查看内容
- python - 无法将我的 MySQL 数据库与 Django 一起使用 - 运行迁移命令后出现 ValueError
- java - 如何从边界框值计算 x,y 坐标
- angular - 如何修复类型'{静态:布尔值;}' 不能分配给 '{ read?: any; 类型的参数。}'。在角度?
- java - 在谷歌地图集成中获取空指针异常