在我的集线器的 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/