从异步函数中的列表中删除项目

uni*_*orn 2 c# linq lambda runtime race-condition

我正在编写一个函数,通过在列表中保留订阅 ID 列表来删除所有订阅。

我遇到了问题

     foreach (var subscriptionId in _listOfSubscriptionIds)
     {
           await _hubProxy.Invoke("removeSubscription", subscriptionId);
           _listOfSubscriptionIds.Remove(subscriptionId);
     }
Run Code Online (Sandbox Code Playgroud)

因为在 foreach 循环中修改列表会引发错误。我在这个社区上发现我可以这样做,foreach(var subscriptions in _listOfSubscriptionIds.ToList())但这在空间复杂性方面听起来很糟糕,特别是如果有大约 10000000 订阅者。

所以我将其更改为以下内容:

    public async Task RemoveSubscription (string subscriptionId)
    {
        await _hubProxy.Invoke("removeSubscription", subscriptionId);
        _listOfSubscriptionIds.Remove(subscriptionId);
    }
    public void RemoveAllSubscriptions()
    {
        _listOfSubscriptionIds.ForEach(async subscriptionId => await RemoveSubscription(subscriptionId));
    }
Run Code Online (Sandbox Code Playgroud)

一些问题:

  1. 我认为第二个在空间和时间复杂度方面更好,但我是对的吗?

  2. 在第二种情况下是否存在我可能遗漏的竞争条件?

  3. 在RemoveSubscription中,_listOfSubscriptionIds.Remove(subscriptionId)也等待,因为它在异步函数中......?

编辑:我没有分享的一个导致调用_listOfSubscriptionIds.Clear()或迭代出现_listOfSubscriptionIds问题的细节是,可能有多个客户端_hubProxy,每个客户端都有其独特的订阅集。

客户端 1 的 subscriptionIds {a, b, c, d, e}

具有 subIds {f, g, h, I, j, k} 的客户端 2

客户端 3 具有子 ID、{l、m、n、o、p} 等。

这意味着当我调用 client1.RemoveAllSubscriptions() 时,它也可能会清除 client2、client3 等的 subscriptionId。

Joh*_*lay 5

您的ForEach操作本质上是“一劳永逸”;它不能确保所有任务都能完成。

您可能最好反向使用 for 循环,并保留任务以便可以等待它们:

var tasks = new List<Task>();
for (int i = _listOfSubscriptionIds.Count - 1; i >= 0; i--)
{
    tasks.Add(RemoveSubscription(_listOfSubscriptionIds[i]));
}

await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

反向迭代可确保特定索引处的值在循环期间不会更改。