【问题标题】:How to stop a "black box" operation?如何停止“黑匣子”操作?
【发布时间】:2011-02-11 00:57:57
【问题描述】:

我正在使用一个异步委托,它调用一个将 xml 文件加载到 XPathDocument 中的方法。如果 xml 太大而无法放入内存,则它永远不会完成加载。如果 xml 文件成功加载到 XPathDocument 中,则以下代码有效。我已经能够使用一个计时器事件来执行 asyncXpath.EndInvoke(result) 语句并且可以结束 CreateDocument 方法,但它不会停止加载 XPathDocument。我的结论是,我唯一能做的就是发出 Application.End 语句来终止应用程序。有谁知道如何停止黑盒操作,例如加载 XPathDocument。

delegate bool AsyncXpathQueryCaller(string xmlfile 

bool found = false; 
AsyncXpathQueryCaller asyncXpath = new 
AsyncXpathQueryCaller(CreateDocument); 
IAsyncResult result = asyncXpath.BeginInvoke(xmlfile, null, null); 
while (!result.IsCompleted) 
{ 
result.AsyncWaitHandle.WaitOne(100, false); 

} 
found = asyncXpath.EndInvoke(result);


private bool CreateDocument (string xmlfile)
{
XPathDocument doc = new XPathDocument(xmlfile);
}

【问题讨论】:

  • 如果文档那么大,我建议使用只进阅读器来读取/处理数据。这会更快,并且在您阅读文件时如有必要也可以中断。
  • @Tim - 创建 XPathDocument 对象的方法只有这么多,所有这些都涉及让构造函数控制文件加载。
  • @KeithS - 我在想更多的 XmlReader ...如果有问题的文档那么大,它可以分块读取,如果一次需要一个块,可以转换/解析/等。不确定它是否适合这种情况,这就是我发表评论而不是答案的原因。
  • 我需要在加载 xml 后进行 xpath 查询,并且我需要向后看的轴,例如祖先::,所以 XPathDocument 或 XmlDocument 是我唯一的选择(在 .net 中)。
  • 那么,你是在问如何杀死一个线程?因为我想我听说你不能在不杀死整个过程的情况下可靠地做到这一点......

标签: c# .net xml xpath


【解决方案1】:

在尝试加载它并检查大小之前使用FileInfo 怎么样?如果它太大,请跳过它。

类似这样的:

FileInfo fi = new FileInfo(xmlfile);
if(fi.Length < /*some huge number*/)
{
  //load the file
}

【讨论】:

  • 如果您可以定义“太大”,我会支持您的答案。因为我怀疑你能做到,所以我很想投反对票。
  • 我突然想到,这只会降低问题的可能性。在某些时候,算法会猜错并尝试加载一个不起作用的文件。所以这并没有解决 OP 的问题,它只是给了他两个(弄清楚一些巨大的数字是什么,他们拒绝大于该数字但仍然可以工作的文件)。跨度>
  • 不,我的解决方案很好。您只是对我说您的解决方案很差(确实如此)感到不安。我坚持我所说的,但如果这能让你感觉更好,你就会更有力量。
  • 如果您能在您的 一些巨大的数字 评论中准确阐明发生了什么魔法,我同意您的回答很好。此外,如果您能准确说明为什么终止运行XPathDocument 构造函数的线程是一个坏主意,我同意您的观点,即我的回答很差。随意做一个或两个(不过,祝你好运解决停机问题)!
【解决方案2】:

您可以声明一个 FileStream 并将其提供给构造函数,但在此之前,请查看其 Length 属性,如果它太长,则返回一个错误。

【讨论】:

  • 用 xml 没有办法知道多长是太长了。它取决于xml的结构和内容而不是文件长度。我对 xml 的任何特征一无所知 - 可能是任何东西。
  • 我不同意;文件长度与文件内容成正比。我能想到的只有两件事会导致一个相对较小的文件在你面前炸毁:对于解析器而言,标签嵌套得太深(不太可能),以及递归实体定义解析出的“十亿笑”类型的漏洞利用到一个 bajillion-character 值。
  • 文件可能对于可用内存来说太大了。有没有办法杀死 .net 在创建 XPathDocument 时创建的线程?
  • @Bill,你是比我聪明,还是没有道理?
【解决方案3】:

正如 Abe Miessler 所建议的,在尝试将文件加载到 XPathDocument 之前检查文件大小是明智的。

如何决定应该是什么限制

没有确切的规则,但我听说人们应该将文件大小乘以 5,然后结果接近 XmlDocument 加载/解析文本所需的内存。

【讨论】:

  • 好的,假设您可以确定XmlDocument 将使用多少内存。你怎么知道这是否“太多”?
  • @Gabe:大多数操作系统通过特定的函数/API 提供有关当前可用内存的信息
  • “当前可用内存”是什么意思?它是可用的物理内存吗?虚拟内存?到底有什么好处?当您处理完 XML 时,可能已经释放或分配了更多内存。
  • @Gabe:这可能是一个反复试验的过程,因为 5 比 1 规则不是绝对的,但是一旦确定了正确的因素,那么人们几乎可以完全控制这个过程。至于内存分配的动态,是的,你是对的,但是如果怀疑所有可用内存是否足以解析/处理 XML 文档,那么同时运行其他任何东西是不明智的。
  • 我解析了一个 20MB 的 XML 文件,发现它占用了大约 60MB 的内存。然后我添加了一些空格,使标签名称更长,并将其保存为 UTF-16。尽管在语义上它是同一个文件,但它增长到了 90MB,但它仍然在解析时只消耗了大约 60MB 的内存。换言之,5:1 规则毫无意义。
【解决方案4】:

编辑:我刚刚意识到KeithS 已经接近一个好的答案。基本思想是你调用XPathDocument constructor,它接受一个Stream,它包装了一个FileStream。传递给它的对象应该实现Read(byte[], int, int) 函数来调用包装好的FileStreamRead 函数,或者如果操作超时则抛出异常。这是一个代码示例:

class XmlStream : FileStream
{
    DateTime deadline;

    public XmlStream(string filename, TimeSpan timeout)
           : base(filename, FileMode.Open)
    {
        deadline = DateTime.UtcNow + timeout;
    }

    public override int Read(byte[] array, int offset, int count)
    {
        if (DateTime.UtcNow > deadline)
            throw new TimeoutException();
        return base.Read(array, offset, count);
    }
}

这里有一些代码读入document,但在 1 秒后超时:

    bool found = true;
    using(var stream = new XmlStream(document, TimeSpan.FromSeconds(1)))
    try
    {
        xpath = new XPathDocument(stream);
    }
    catch (TimeoutException)
    {
        found = false;
    }

如果您创建一个单独的线程而不是执行BeginInvoke,您可以在计时器计时(或有人单击“取消”)时中止线程。虽然通常不建议中止线程,因为它可能持有锁或全局数据处于不一致状态,但在这种情况下应该没问题,因为您的线程不会持有锁或访问全局数据。

以下是此方法的代码,与上一个示例的作用相同:

    bool found = false;
    thread = new Thread(() =>
    {
        xpath = new XPathDocument(document);
        found = true;
    });
    thread.Start();
    thread.Join(TimeSpan.FromSeconds(1));
    thread.Abort();

如果您对在自己的应用程序域中中止线程感到不舒服,您可以在另一个应用程序域中创建文档,如果时间过长,可以调用AppDomain.Unload。这将需要一些编组,但可能不会有太多开销。

能够杀死一个进程的最终方法是在一个单独的进程中运行它并使用某种远程接口来访问它。不过,这可能比其他选项更混乱,因为您必须担心查找可执行文件、传递参数、某些用户终止它等等。

【讨论】:

  • 你说得对,中止线程不好。我不同意这是最好的选择...
  • @Abe Miessler:我同意你的观点,但请注意,这回答了 OP 问题,其他人正在给出建议。
  • 我尝试了您的计时器建议以中止线程。它停止调用的方法,但加载 xpathdocument 的“黑匣子”过程仍在继续。任务管理器仅显示剩余 3Mb 内存(开始时大约 800Mb)。 xml 文件大小为 266Mb。也许 xmpathdocument 加载在由 .net 处理的单独线程中,而不是我的应用程序。我什至尝试了 application.exit ,它仍然没有停止它。
  • @Bill:您是使用单独的线程来运行BeginInvoke,还是只是在单独的线程上调用new XPathDocument?如果 CPU 使用停止,则说明您做对了。内存将被消耗,直到垃圾收集运行(调用 GC.Collect() 手动完成。
  • @Bill Seacham:我已经编辑了我的答案,以便为两种不同的方法提供工作示例代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-07
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多