微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

处理来自套接字流的数据时集合被修改错误

如何解决处理来自套接字流的数据时集合被修改错误

我正在尝试从 Binance 聚合数据流中捕获刻度级别数据。一旦日期发生变化,函数就会处理前一天的数据。我正在使用 Task.Run 创建另一个线程,因为当天的新数据仍在流入并需要捕获。但是,我收到“集合已修改,枚举操作可能无法执行”的消息。在处理数据的函数的 foreach 循环中。我不确定为什么在传递给处理函数后会修改 TradeData?不确定这是否重要,但我在不同的线程上捕获了大约 40 个不同的符号。

private static void Start()
{
    foreach (SymbolData symbol in symbolData)
    {
         Task.Run(() => SubscribetoSymbol(symbol.symbol));
    }
}

private static void SubscribetoSymbol(string symbol)
{
    Dictionary<decimal,decimal> TradeData = new Dictionary<decimal,decimal>();
    DateTime lastTrade = DateTime.UtcNow;
    try
    {
         var socketClient = new BinanceSocketClient();
         socketClient.FuturesUsdt.SubscribetoAggregatedTradeUpdates(symbol,data =>
         {
              if (data.TradeTime.Date > lastTrade.Date)
              {
                   Task.Run(() => ProcessData(symbol,lastTrade.Date,TradeData));
                   TradeData.Clear();
              }
              lastTrade = data.TradeTime;

              if (TradeData.ContainsKey(data.Price))
              {
                   TradeData[data.Price] = TradeData[data.Price] + data.Quantity;
              }
              else
              {
                   TradeData[data.Price] = data.Quantity;
              }
         });
     }
     catch { }
}


private static void ProcessData(string symbol,DateTime date,Dictionary<decimal,decimal> TradeData)
{
     foreach (var price in TradeData)
     {
     //Error: Collection was modified enumeration operation may not execute.
     }
}

解决方法

您重复调用 Task.Run,​​结果有多个线程处理您的 tradeData 对象,这就是您看到集合修改异常的原因。

我建议您重新考虑代码设计,以免多个线程处理相同的对象。如果您绝对必须让线程处理相同的对象,则应使用锁:

private static object _objlock = new object();

private static void ProcessData(string symbol,DateTime date,Dictionary<decimal,decimal> tradeData)
{
    lock (_objlock)
    {
        foreach (var price in tradeData)
        {
     
        }
     }
}

使用锁时要小心死锁。我建议阅读 C# 中的锁和多线程。

,

当您在字典上开始 foreach 循环时,外部正在“清除”它。 您应该复印一份并发送给ProcessData

Task.Run(() => ProcessData(symbol,lastTrade.Date,tradeData.ToArray()));

并接收副本:

private static void ProcessData(string symbol,KeyValuePair<decimal,decimal>[] tradeData)
{
    foreach (var price in tradeData)
    {
// ... do your thing here
    }
}

或者,更简单的方法是重新创建一个新字典而不是清除它:

//tradeData.Clear();
tradeData = new Dictionary<decimal,decimal>();
,

有一次我在 foreach 循环中从列表中删除值时遇到类似问题,我已将其更改为简单的 for 循环,这有助于您尝试。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。