新的 FileStream 已关闭/处置?

发布于 2024-09-26 23:13:22 字数 1502 浏览 0 评论 0原文

我使用 FileMode.OpenFileAccess.Read 打开一个 FileStream。不久之后,我调用一个函数来处理文件的内容。我使用 Invoke 进行调用,因为调用来自 Thread,并且该函数必须将结果放在 Form 上。该函数接受任何类型的 Stream(我也用 MemoryStream 来调用它,没有问题),并使用 XmlTextReader 读取 FileStream 中的 XML ,但在极少数情况下,由于未知原因,即使第一个 Read() 也会抛出 ObjectDisposeException ,并且如果流已经存在,则流的 CanRead 属性会返回 false关闭。

Thread 中,FileStream 是一个本地 using 变量,所以我认为其他线程不应该能够关闭它,而且我也不认为在 Invoke 返回之前不要关闭它。没有抛出异常,因此文件肯定存在(因为没有FileNotFoundException)并且应该可以正确访问(因为没有UnauthorizedAccessException)和 IOException )。

为什么我的 FileStream 有时在打开后看起来仍然关闭?

(这可能很重要,因为我在带有 Compact Framework 3.5 的 Windows CE 5 设备上运行我的代码,但我还无法在带有 XP 的台式电脑上重现相同的行为。)

编辑: 我知道,这个 Invoke 很丑陋,但仅此一点不能成为失败的理由,不是吗? (而且,在大多数情况下,它根本不会失败。)

//the code in the thread
//...
using (FileStream fs = File.Open(assemblyPath + "\\white.xml", FileMode.Open, FileAccess.Read))
{
    mainForm.Instance.Invoke(new DataHandler(mainForm.Instance.handleData), new object[] { fs });
}
//...

//and the handler
public void handleData(Stream stream)
{
    infoPanel.SuspendLayout();
    try
    {
        using (XmlTextReader xml = new XmlTextReader(stream))
        {
            //it doesn't matter what is here
        }
    }
    catch{}
}

I open a FileStream with FileMode.Open and FileAccess.Read. Shortly after that I call a function to handle the file's contents. I use Invoke to make the call because the call comes from a Thread and the function has to put the results on a Form. The function accepts any kind of Stream (I call it with MemoryStreams too without a problem) and uses XmlTextReader to read the XML in the FileStream, but on rare occasions for unknown reasons even the first Read() throws an ObjectDisposedException and the stream's CanRead property returns false if the stream was already closed.

In the Thread the FileStream is a local using variable, so I don't think another threads should be able to close it, and I don't close it until the Invoke returned. There are no Exceptions thrown so the file is definetly there (since there is no FileNotFoundException) and should be accessed properly (since there is no UnauthorizedAccessException and IOException).

How could my FileStream still look closed sometimes just after opened?

(It might matter that I'm running my code on a Windows CE 5 device with Compact Framework 3.5 and I wasn't able to reproduce the same behaviour on my desktop PC with XP yet.)

EDIT:
I know, that this Invoke is ugly but that alone can't be a reason to fail, can it? (And, in most of the cases it doesn't fail at all.)

//the code in the thread
//...
using (FileStream fs = File.Open(assemblyPath + "\\white.xml", FileMode.Open, FileAccess.Read))
{
    mainForm.Instance.Invoke(new DataHandler(mainForm.Instance.handleData), new object[] { fs });
}
//...

//and the handler
public void handleData(Stream stream)
{
    infoPanel.SuspendLayout();
    try
    {
        using (XmlTextReader xml = new XmlTextReader(stream))
        {
            //it doesn't matter what is here
        }
    }
    catch{}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怪异←思 2024-10-03 23:13:22

我能想到的原因只有一个:工作线程被中止。这将运行 using 语句生成的finally 块并关闭文件。如何中止它是次要问题。线程的 IsBackground 属性是否设置为 true?程序是否因其他地方未处理的异常而崩溃并关闭?当然只是猜测。

There's one reason I can think of: the worker thread got aborted. This will run the finally block generated by the using statement and close the file. How it could be aborted is a secondary question. Is the thread's IsBackground property set to true? Is the program bombing on an unhandled exception elsewhere and shutting down? Just guesses of course.

木格 2024-10-03 23:13:22

当然,这是预期的行为。您调用 Invoke,它将调用编组到另一个线程。然后,调用线程继续运行,并且 using 块退出,在流上调用 Dispose。此 Dispose 发生在您使用 UI 线程中的流完成之前(也可能是开始之前)。这些操作的确切时间将取决于处理器负载和其他一些因素,但这肯定是不安全的。

要么不要将流放入 using 块中,要么最好让线程执行读取操作并通过 Invoke 将结果传递给 UI。

编辑

正如汉斯在评论中指出的,上面的解释应该是针对 BeginInvoke 调用,它在下面调用 PostMessage。另一方面,Invoke 使用 SendMessage。两者可能都使用一些 WM_COPYDATA 恶作剧(我没有看到)来整理数据。

Invoke 调用应该执行您发布的整个处理程序,尽管您看到的行为表明情况并非如此。从您发布的代码来看,我们没有真正的方法来确定是什么关闭了流。

我仍然会重构您在这里所做的事情,因为现在您将 UI 和工作线程与读取器操作绑定在一起。我会在工作线程中完成读取工作,然后将结果传递给 UI。这将减少读取器工作导致 UI 不稳定的可能性,并消除在您读取流时流被关闭的可能性。

Sure, this is expected behavior. You call Invoke, which marshals the call to another thread. The calling thread then continues to run and the using block exits, calling Dispose on the stream. This Dispose is happening before you are done (and maybe before you start) using the stream in the UI thread. The exact timing of these actions is going to depend on processor load and some other factors, but it's certainly unsafe.

Either don't put the stream in a using block or better yet have the thread do the read and pass the results to the UI via Invoke.

EDIT

As Hans points out in the comment, the above explanation should be for a BeginInvoke call, which underneath calls PostMessage. Invoke, on the other hand, uses SendMessage. Both propbably uses some WM_COPYDATA shenanigans (I've not looked to see) to marshal the data.

The Invoke call should be executing the entire handler you have posted, though the behavior you see indicates otherwise. From the code you posted there's no real way for us to determine what is closing the stream.

I would still refactor what you've done here because right now you're tying up both the UI and worker threads with the reader operation. I'd do the read work in the worker thread and then pass the results to the UI. This would decrease the odds of the reader work causing UI choppiness and would eliminate the possibility of the stream getting closed while you're reading from it.

丑疤怪 2024-10-03 23:13:22

我在我正在开发的一些嵌入式板(ARM)上看到了同样的问题。然后我创建了一个小测试。

以下代码(不涉及任何线程!)崩溃:

    using (var w = new StreamWriter(File.Create("file.txt"), System.Text.Encoding.UTF8))
    {
        for (int i = 0; i < 1000; i++)
        {
            w.WriteLine("Test");
        }
    }

但是此代码不会崩溃:

    using (var w = File.CreateText("file.txt"))
    {
        for (int i = 0; i < 1000; i++)
        {
            w.WriteLine("Test");
        }
    }

因此,我的猜测只能是底层本机代码处理文本文件的方式与使用 File.Create() 打开文件时不同。这两个文件都以 UTF-8 编写,因此编码没有区别。

顺便说一句:抱歉,我的答案晚了一年,但我希望它能对某人有所帮助

I saw the same issue on some embedded board (ARM) I'm working on. Then I created a little test.

The following code (not involving any Threads!) crashes:

    using (var w = new StreamWriter(File.Create("file.txt"), System.Text.Encoding.UTF8))
    {
        for (int i = 0; i < 1000; i++)
        {
            w.WriteLine("Test");
        }
    }

This code however does not crash:

    using (var w = File.CreateText("file.txt"))
    {
        for (int i = 0; i < 1000; i++)
        {
            w.WriteLine("Test");
        }
    }

So, my guess can only be that the underlying native code treats text files differently than when you open the file using File.Create(). Both files are then written in UTF-8, so there is no difference about the encoding.

BTW: sorry I'm one year late on the answer, but I hope it'll help somebody

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文