【问题标题】:Schema validation error / Thread safety of XmlSchemaSet?架构验证错误/XmlSchemaSet 的线程安全?
【发布时间】:2009-09-28 12:23:08
【问题描述】:

下午好,

XML 模式验证 sn-p 在开发/问答环境中运行良好,但在生产环境中产生了一些奇怪的验证结果。通常的怀疑是代码对于线程来说是不安全的,并且生产场景的额外负载正在清除错误。

具体情况如下。考虑要验证的 XML 是:

<mssql:spExecute type="ResultSet" xmlns:mssql="urn:namespace">
    <mssql:actor>IPASS</mssql:actor>
    <mssql:connection>ConnectionString</mssql:connection>
    <mssql:storedProcedure>dbo.RedFox</mssql:storedProcedure>
</mssql:spExecute>

在一整天的过程中,大约 300 次执行(>2M)将产生以下异常:

System.Xml.Schema.XmlSchemaValidationException 未声明“http://www.w3.org/2000/xmlns/:mssql”属性。

模式验证器似乎抱怨命名空间声明。

代码结构如下:

  • 有一个静态的 XmlSchemaSet 实例;
  • XmlSchemaSet 实例的初始化是以线程安全的方式完成的;
  • 每个工作线程都使用相同的 XmlSchemaSet;
  • 验证发生在 XmlSerializer.Deserialize() 调用期间,使用 XmlReader 和 XmlReaderSettings 初始化 ValidationType.Schema。

对可能导致验证因命名空间声明而窒息的原因有什么想法吗?

【问题讨论】:

    标签: c# thread-safety


    【解决方案1】:
    1. 您所描述的是受控实验的结果还是 从生产环境观察? 如果是后者,请尝试在受控设置中复制它以确保其他一些 错误不是真正的原因。

    2. The documentation for XmlSchemaSet 说:

      “不保证任何实例成员都是线程安全的。”

      这包括属性读取和“只读”方法。 所以我们不能假设XmlSchemaSet 的只读使用是线程安全的。 直到微软提供明确允许共享相同编译的实现 线程之间的模式表示,唯一安全的做法是不共享。

    3. OTOH 共享单个编译的不可变 XmlSchemaSet 来验证似乎是合乎逻辑的 多个XmlDocument(或XmlReader)实例。 这是一个非常明智的场景,所以我不明白为什么它没有明确 允许并记录在案。

      (更新:显然,Java's standard XML library 明确保证了这一点。 为什么在 .NET 中不是这样?)

    【讨论】:

    • 文档不再提及,我们应该明白它现在是线程安全的吗?
    【解决方案2】:

    这可能在 .NET 4.0 中得到修复,但未记录在案。

    我在生产 Web 服务中遇到了同样的错误。错误类似,并且在活动高峰期间是随机的:“未声明 xxx 元素”。但是在我的例子中,我们只是使用循环读取命令验证 XML,而不是序列化对象。

    该服务是在 .NET 3.5 下构建/运行的,在开发环境中,我可以通过启动 5 个线程并同时进行服务调用来轻松复制此错误。大约每 10 个请求中就有 1 个发生错误。

    当我坐下来解决这个问题时,我做的第一件事就是将服务升级到 .NET 4.0。一旦我这样做了,我就不能再产生这个错误了。我增加了线程数并删除了服务限制 - 仍然没有错误。

    虽然当前文档仍表明 XmlSchema 实例操作不能保证线程安全,但似乎在 3.5 和 4.0 之间发生了一些框架更改,提高了 XmlSchema 上读取/验证类型操作的线程安全性。

    【讨论】:

    • 另外请注意,如果这不起作用,我可能会在启动时创建 XmlSchema 对象的列表并包装对它们的访问,以便每个线程只使用一个。我相信创建对象需要一些成本,最好不要在每个请求上创建一个。
    • 好资料!我也做了一个类似的测试,结果似乎表明运行 100 个并发线程没有中断,每次读取和验证 XML 文档,共享相同的 XmlSchemaSet。但是即使这样,我也不会相信它,直到 MSDN 明确表示它是线程安全的使用。拥有一个预编译模式对象池并为每个线程使用并在完成后返回池中获取一个似乎是一个好主意。
    【解决方案3】:

    我遇到了同样的问题。 XML验证似乎不是只读操作,本文指出:

    http://blogs.msdn.com/xmlteam/archive/2009/04/27/xmlschemaset-thread-safety.aspx

    锁住它为我解决了这个问题。

    【讨论】:

    • 没有。它不是。再读一遍。文章说验证只对XmlSchemaSet 使用只读操作,因此多个线程可以共享单个XmlSchemaSet 实例而无需显式锁定。它指出a)XmlSchemaSet 构造不是线程安全的,b)您需要确保您提供的ValidationEventHandler 回调是线程安全的。两者都很明显。它没有说的是为什么它假定XmlSchemaSet 上的只读操作确实是线程安全的。文档说“任何实例成员[包括属性读取--ts]都不能保证是线程安全的。”
    • 您能详细说明一下吗?只读的定义是什么? .NET Framework 中“只读操作”的含义是否有任何官方定义? 如果一个操作是只读的,怎么说——从外部查看一个类?请记住,任何类型的惰性求值都会导致一个操作——在外部读取时—— only---改变对象的内部状态。对于在使用前编译的XmlSchemaSet,期望所有读取都是真正的只读是有意义的。问题是,文档并没有这么公开,我不确定我是否可以安全地缓存已编译的架构。
    猜你喜欢
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    相关资源
    最近更新 更多