【问题标题】:Mysterious behavior of Dictionary<TKey, TSource>Dictionary<TKey, TSource> 的神秘行为
【发布时间】:2012-05-29 20:26:18
【问题描述】:

我正在开发一个基于 Asp.net MVC 3.0 的大型系统,并使用 Mono-2.10.8 (Windows 7)。

直到几天前一切都很好。

在我的 API 中,我有几个使用字典的实用程序类。比如这样的:

public static class KeyUtility  
{
  static KeyUtility() {
    Alphabet = new[] {
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
      'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 
      'T', 'U', 'V', 'X', 'Y', 'Z', '0', '1', 
      '2', '3', '4', '5', '6', '7', '8', '9'
    };

    ReverseAlphabet = Alphabet
      .Select((c, i) => new { Char = c, Value = i })
      .ToDictionary(k => k.Char, v => (byte) v.Value);
  }

  internal static char[] Alphabet;      
  private static IDictionary<char, byte> ReverseAlphabet;

  public static string ToKey(byte[] key, int groupSize)
  {
    //Accessing Alphabet to generate string key from bytes
  }

  public static byte[] FromKey(string key)
  {
    //Accessing ReverseAlphabet to get bytes from string key
  }
}

我随机得到这样的异常:

System.IndexOutOfRangeException: Array index is out of range.
at System.Collections.Generic.Dictionary`2<char, byte>.TryGetValue (char,byte&) <0x000a1>
at MyAPI.KeyUtility.FromKey (string) <0x0009a>
at MyApp.Controllers.AboutController.Index () <0x002df>
at (wrapper dynamic-method) object.lambda_method (System.Runtime.CompilerServices.Closure,System.Web.Mvc.ControllerBase,object[]) <0x0002f>
at System.Web.Mvc.ActionMethodDispatcher.Execute (System.Web.Mvc.ControllerBase,object[]) <0x0001b>
at System.Web.Mvc.ReflectedActionDescriptor.Execute (System.Web.Mvc.ControllerContext,System.Collections.Generic.IDictionary`2<string, object>) <0x000ff>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod (System.Web.Mvc.ControllerContext,System.Web.Mvc.ActionDescriptor,System.Collections.Generic.IDictionary`2<string, object>) <0x00019>
at System.Web.Mvc.ControllerActionInvoker/<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12 () <0x00066>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter (System.Web.Mvc.IActionFilter,System.Web.Mvc.ActionExecutingContext,System.Func`1<System.Web.Mvc.ActionExecutedContext>) <0x000b8>

大多数时候一切都很好,KeyUtility 工作正常,但在极少数情况下我会遇到这样的异常。 尽管看起来像线程安全问题,但字典 ReverseAlphabet 始终只能以只读方式访问,而从不用于写入。一旦在静态构造函数中创建它,就只能使用TryGetValue 访问它。正如我从 MSDN 文章中了解到的那样,在这种情况下它应该是线程安全的。 而且,我之前没见过这个问题。

我应该看什么?我什至无法创建复制品,因为我完全不知道出了什么问题。

Mono-2.10.8 和更早的版本被证明是稳定的字典。我已经密集使用它几年了,以前从未见过这种异常。

如何解决这个问题?

UPD:

我记得在麻烦开始的时候,我所做的是静态链接单声道与我的可执行文件(我将单声道嵌入到我的应用程序中)。我只是下载了单声道的来源。除了我将 libmono 的输出设置为静态库之外,没有任何更改就编译它。我还与 libeay32 和 sqlite3 相关联。所有多线程(MT)。也许这种变化会影响应用程序?不幸的是,我无法在独立单声道下检查这个。在此之前,我正在动态链接所有库,一切都很好。

UPD2: 以下是完整来源的链接:http://pastebin.com/RU4RNCki

【问题讨论】:

  • 您确定它以读取方式使用吗? 绝对确定它永远不会以任何方式更新?
  • 看起来是线程问题...
  • 我使用dotPeek 1.0查看了TryGetValue,因此我只能建议必须从要引发此异常的内部数组。或者 Mono 对通用字典的解释可能有缺陷?
  • 能否请您发布完整的源代码?
  • 尝试记录传递给 FromKey(string key) 方法的“key”值

标签: c# exception dictionary mono thread-safety


【解决方案1】:

我所做的是静态链接单声道与我的可执行文件

你已经知道我认为的问题,那肯定会破坏单声道。静态链接时不再发生的最重要的事情是 DllMain() 回调,每当新线程开始在进程中执行时,Windows 都会进行回调。这就是 CLR 了解可能执行托管代码的线程的方式。静态构造函数确实是一种可能的故障模式,必须阻止线程执行类中的任何代码,直到 cctor() 完成执行。如果 CLR 不知道线程,它就无法执行此操作。

如果您想完成这项工作,那么您至少需要在您自己的 DllMain() 函数中为 mono_thread_info_attach() 调用提供替代方案。总的来说,我会说这是一种太多的优化。

【讨论】:

  • 你完全正确。我发现一个讨论重复了你所说的 mono.1490590.n4.nabble.com/Mono-static-library-td3546774.html 。但这将我引向另一个问题。如果我强制 libmono 使用 SGen 而不是 Boehm 它可以工作吗?如果是的话,我该怎么做...现在我正在尝试使用 HAVE_SGEN_GC 编译 libmono 但缺少很多外部依赖项...
  • 是的,这也是 GC 的问题。但这不是您的问题,您遇到了静态构造函数的问题。所以不,SGen 不会解决这个问题。我没有看到有人在那个单页中提供解决方案,如果您根本没有 DLL,这肯定是一个无法解决的问题。你最好把它作为一个可行的选择写下来。
  • 当然,我知道这不完全是我的问题。我仅在静态链接方面指出它实际上是我问题的真正原因。我认为根据它的设计(我没有调查过),如果 SGen 不使用不太可能的 DllMain,它可以解决问题。无论如何,windows 端口仍在开发中,所以今天没有其他选择。感谢您提供有用的答案和 cmets,Hans
猜你喜欢
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 2016-06-14
相关资源
最近更新 更多