使用信号器更新列表

本文关键字:列表 更新 信号器 | 更新日期: 2023-09-27 18:36:23

我正在编写一个信令客户端-服务器应用程序。

服务器维护项目列表。

客户端显示列表并允许用户向列表中添加项。

假设服务器有一个返回整个列表的GetList方法,客户端有一个NewItem方法,每当客户端向列表中添加项时,服务器都会调用该方法。

这是客户端代码:

public void Init()
{
    hubConnection = new HubConnection(url);
    proxy = hubConnection.CreateHubProxy(hubName);
    proxy.On<String>("NewItem", OnNewItem);
    hubConnection.Start();
    list = proxy.Invoke("GetList");
}
private void OnNewItem(string item)
{
   list.Add(item);
}

我担心以下情况:

  1. 客户端 A 已连接
  2. 客户端 B 连接到服务器
  3. 客户端 A 将项目添加到列表中,同时客户端 B 发送 GetList 消息以获取列表

此时,服务器中有 2 个线程。线程 A 正在添加一个项目,然后使用新项目向客户端发送一个事件。线程 B 正在加载列表并将其发送回客户端 B。

假设线程 B 在线程 A 添加新项目之前从数据库加载列表。然后有一个上下文切换,线程 A 添加了该项目,并向所有客户端发送了包含新项目的消息。

现在,线程 B 恢复并将列表(不带新项)发送到客户端 B。

从客户 B 的角度来看,它得到了一个带有新项目的事件,他不知道如何处理,然后它得到了列表但没有新项目。

客户端 B 可以忽略新项目消息(因为它是在列表初始化之前出现的),但随后他将始终缺少 1 个项目,因为服务器永远不会为此项目发送另一个事件。

B 还可以将消息添加到空列表中,然后添加它获得的列表。但是,如果客户端 A 没有添加项目,而是删除了一个项目,该怎么办?在初始化列表之前,B 将如何处理删除项事件?

使用信号器更新列表

您尝试执行的操作听起来可能有些困难,尤其是当您允许多个客户端同时任意编辑列表时。如果是这种情况,您将考虑实现运营转换算法。虽然这已经被相当广泛的研究,但它远非微不足道。

如果您只是尝试订阅单个来源所做的编辑,则处理起来要容易得多。您可以利用这样一个事实,即您将订阅await hubConnection.Start();完成后发送的任何更新。

这意味着,只要您在尝试调用"GetList"之前正确awaithubConnection.Start()的调用,在客户端调用"GetList"期间或之后对"NewItem"进行的任何服务器端调用都将被客户端OnNewItem回调接收。

您在OnNewItem中收到的项目仍可能与"GetList"的响应重复。要解决此问题,请为添加到列表中的每个项目提供一个单调递增的 ID,该 ID 与项目本身一起传递给OnNewItem回调。"GetList"响应还应包括其最近添加的项目的 ID。

在收到对"GetList"的响应之前,您将OnNewItem接收的任何项目保留在临时列表中。获得对"GetList"的响应后,您可以在临时列表中添加 ID 大于添加到"GetList"响应的最新项目 ID 的任何项目。忽略 ID 小于或等于"GetList"响应中返回的 ID 的任何项目。