【问题标题】:Collection was modified in async application?在异步应用程序中修改了集合?
【发布时间】:2019-11-09 00:33:13
【问题描述】:

我的 .NET 4.6.2 winforms 应用程序中有以下代码:

paramList 是一个OrderedDictionary

if ( paramList != null ) {
     foreach ( DictionaryEntry param in paramList ) {
         command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
     }
}

在我开始在应用程序中实现更多异步/多线程处理之前,它总是很有效。现在,当上面的代码运行时,我偶尔会收到以下错误:

System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

我似乎根本没有修改集合,所以我不知道它为什么抱怨。

我了解这些帖子中的讨论/解决方案,但似乎不适用于我的:

Collection was modified exception

System.InvalidOperationException: Collection was modified

使这个线程安全的最好方法是什么?

编辑#1

我现在有这个:

private static object syncLock = new object();

[...]

lock ( syncLock ) {
   if ( paramList != null ) {
      foreach ( DictionaryEntry param in paramList ) {
          command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
      }
   }
}

我运行了几次,然后问题又出现了:

即使在lock() 内部,这怎么可能仍然发生?我能做什么?

编辑#2

paramList 总是由调用者创建并传递给函数,例如:

var paramList = new OrderedDictionary();
paramList.Add("RuleID", btRule.RuleID);
paramList.Add("TypeSeq", 0);
paramList.Add("Type", btRule.Action);

bool result = await ExecuteQueryAsync(sql, paramList, connection);

public static async Task<bool> ExecuteQueryAsync(string sql, OrderedDictionary paramList, SqlConnection connection) {
     try {
        using ( SqlCommand command = new SqlCommand(sql, connection) ) {
           command.CommandType = CommandType.Text;

           lock ( syncLock ) {
              if ( paramList != null ) {
                 foreach ( DictionaryEntry param in paramList ) {
                    command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
                 }
              }
           }

           command.CommandTimeout = 0;
           await command.ExecuteNonQueryAsync();
        }
        return true;
     }
     catch ( Exception ex ) {
        [...]
     }
}

【问题讨论】:

  • 在任何线程上对 paramList 的任何修改都会导致此问题,您很可能在某处遇到线程安全问题
  • 你可以尝试locking paramList 在异步方法中访问它的任何地方
  • paramList 是什么?它从何而来?它是什么类型的?
  • 您确实需要向我们提供minimal reproducible example 来诊断此问题。但是,作为临时修复,请尝试在foreach 中的paramList 之后添加.ToList(),看看问题(大部分)是否消失。
  • 我编辑了问题以给出 paramList (OrderedDictionary) 的类型,它没有 ToList()

标签: c#


【解决方案1】:

因为if ( paramList != null ) { 我猜paramList 被不同的演员重用。您需要为每个工作单元使用不同的实例。

【讨论】:

  • 这个解决方案对我来说没有意义,我不知道你在说什么。使用lock() 似乎可以解决它,但现在我得到如下所示的对象引用错误:stackoverflow.com/questions/58784691/…
  • 每个演员都应该有自己的paramList,他们不应该分享。
  • paramList 不共享 - 它由调用者传入,作为函数参数。
  • paramList 是解决这个问题的关键,您并没有向我们展示您在哪里创建它以及如何使用它。我会认为 tymtam 的假设是正确的。
  • Theodor 和 tymtam:请参阅我的编辑 #2。我确切地展示了paramList 是什么,示例用法,以及它是如何传递到函数中的。我认为它从来没有被共享过,因为每个调用函数都有它自己的新实例。有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多