并发问题 - .NET上的Saxon 9.6,XSLT以及自定义XmlResolver和CollectionUriResolvers

时间:2017-08-06 19:37:57

标签: c# xml multithreading xslt saxon

我希望验证我对Saxon的XSLT对象和并发性的理解。

基本上,我需要自定义解析器将特定于请求的数据返回到转换,目前,我正在为每个请求为每个转换创建解析器的新实例。我已经收到了另一个请求中使用的数据的早期报告,这是一个重大问题。

在.NET 4.6(C#),Windows 7 / Server 2012上使用Saxon 9.6.0.6 HE。

我的代码可以为许多并发请求执行许多不同的共享转换。 Saxon编译的XSLT是性能必须的。目前,代码是多线程的(在适当的情况下使用TPL和async),并且不使用锁定(并且如果可能的话想要避免这种情况)。

我偶尔会有数据被错误地泄漏的报告。转换输出中的请求(即,可能是并发问题)。我不确定这是否与自定义XmlResolver或自定义CollectionUriResolver的行为相关联。我正在等待更多信息。我还没有能够重新创建这个问题(如果可以的话,我还会继续处理这个问题并发布更新)。

我们的转换使用fn:doc和fn:collection。

代码在应用程序启动时预编译所有可能的转换。这些可执行文件是共享的。

对于事务中的给定转换,我的代码通过编译的可执行文件的.Load()调用创建一个XsltTransformer对象。这似乎创建了一个查看9.6 HE代码的新对象(这是我期望的)。

接下来,我的代码创建了自定义XmlResolver和CollectionUriResolver的新实例(尚未移至CollectionFinder,但认为这可能以相同的方式运行)并且这些实例填充了相应的特定于请求的文档/值/等进入转型。

这两个解析器的生命周期只有一个XSLT执行 - 它们不会被重用。

我们将解析器与XsltTransform对象关联,这是我知道的唯一方法:

Saxon.Api.XsltTransformer transform = executable.Load();
transform.InitialContextNode = sourceData; 
transform.Implementation.getConfiguration().
    setCollectionURIResolver(collectionResolver);
transform.InputXmlResolver = inputResolver;

在Saxon代码中,看起来像输入解析器是基于实例的,因此不共享(最终看起来像Controller一样存在,如下所示,当XsltTransformer时它本身就是一个新实例是通过Load()创建的。

public XmlResolver InputXmlResolver
{
    set
    {
        controller.setURIResolver(new DotNetURIResolver(value));
    }
}

但是,我担心配置数据可能会被共享,并且在配置对象上设置集合解析器(CollectionFinder看起来是相同的)时,我们可能会遇到并发问题。

在我的自定义解析器响应特定于请求的行为后,实现结果的正确方法是什么?我可以在每个转换中使用一对实例来处理特定于请求的数据,还是必须在请求之间共享解析器(可能将请求ID注入转换以形成传递给解析器的URI的一部分)?

轻微更新 看来你可以直接在控制器上设置CollectionURIResolver('实现'),也可以在配置上设置,这些是内存中截然不同的对象:

transform.Implementation.setCollectionURIResolver(collectionResolverOne);        
transform.Implementation.getConfiguration().
    setCollectionURIResolver(collectionResolverTwo);

但是,在运行时,它是调用的配置解析器(在上面的例子中为collectionResolverTwo)。我不确定控制器副本的用途是什么。

此外,看起来配置数据确实是共享的,因为如果我从同一个可执行文件创建第二个转换器并在配置级别设置它的集合解析器,则会更新第一个转换器使用的解析器。

所以 - 我想我已经发现了我的问题 - 我现在需要知道在我的场景中要做的正确的事情,我需要集合解析器来为每个请求唯一地解析集合(例如,一个请求可能在特定集合中有五个条目,另一个可能有两个条目。)

1 个答案:

答案 0 :(得分:1)

我认为在解释你的问题时,你基本上已经解决了这个问题。 Saxon中的Configuration对象(在API级别支持处理器)是共享的,在任何初始化之后,强烈建议不要使用setURIResolver()等方法更改其状态,因为此类更改将影响正在进行的工作。未定义的方式。

Saxon具有API对象和内部对象,具有一对一的对应关系,并且内部对象不是100%封装的,因为一些用户需要访问更私密的功能。在Java世界中,还有一些JAXP类在概念上类似,但仅限于XSLT 1.0功能。通信是:

整个撒克逊环境的共享信息:

API :处理器内部:配置 JAXP :TransformerFactory

可重用的XSLT编译器,包含用于编译样式表的选项:

API :XsltCompiler 内部:CompilerInfo JAXP :无等效

一个已编译的样式表,可以重复执行(并在多个线程中同时执行)

API :XsltExecutable 内部:可执行文件/ PreparedStylesheet JAXP :模板

单个转换,使用一个样式表转换一个源文档:

API :XsltTransformer 内部:控制器 JAXP :Transformer

Saxon中的一些配置选项仅在配置级别可用,例如,可用的整理URI集合在此级别定义,并且不能从一个转换到另一个转换。

但是,URIResolvers通常可以在XsltTransformer / Controller级别定义。它们也可以在处理器/配置上设置,但如果你想在整个过程中使用相同的那个,那么这只是默认设置。在您的情况下,您应该在控制器级别设置它们。