【问题标题】:APM and the unresolved System.AccessViolationException mystery on breakpointsAPM 和断点上未解决的 System.AccessViolationException 之谜
【发布时间】:2014-07-07 07:17:28
【问题描述】:

考虑以下函数:

static void Main(string[] args)
{
    FileStream fs = new FileStream("e:\\temp.txt", FileMode.Open);
    int size = (int)fs.Length;

    byte[] data = new byte[size];
    IAsyncResult result = fs.BeginRead(data, 0, size, new AsyncCallback(Callback), fs);

    Console.ReadLine();
}

问题 1: 如果我在 BeginRead 行设置断点,并在调试模式下运行程序,我会收到以下错误:

MyApp.exe 中出现“System.AccessViolationException”类型的未处理异常。

但是,如果我将断点放在 ReadLine 行并执行相同操作,则不会发生错误。我相信将 FileStream 实例作为最后一个参数传递给 BeginRead 函数会导致问题,但是,我不知道那里发生了什么。

---UPDATE1:您可能会问我为什么要尝试将“fs”传递给回调。是的,我可以将它作为成员变量保存,但是如果我要异步读取多个文件怎么办?那样的话,保存一个文件流数组(或列表)是不合理的。

问题2: AFAIK,IAsyncResult 定义如下:

[ComVisible(true)]
public interface IAsyncResult
{
    object AsyncState { get; }
    WaitHandle AsyncWaitHandle { get; }
    bool CompletedSynchronously { get; }
    bool IsCompleted { get; }
}

但是,在跟踪代码时,我注意到 IAsyncResult 上的其他成员:

这怎么可能? IAsyncResult 显然是由 ReadWriteTask 类(在这种情况下)实现的,但是,我不知道那些其他属性的来源。

---更新2: 我尝试使用以下代码来模仿:

public interface IMyInterface
{
    int Prop1 { get; }
}

public class Impl : IMyInterface
{
    public int Prop1 { get { return 101; } }
    public int Prop2 { get { return 202; } }
}

public class MyClass
{
    public IMyInterface GetMyInterface()
    {
        Impl impl = new Impl();
        return impl;
    }
}

为了调用该方法,我只需实例化一个 MyClass 并调用 GetMyInterface 方法,如下所示:

MyClass c = new MyClass();
IMyInterface my = c.GetMyInterface();

但是,当我在 Quickwatch 中查看 my 变量时,我看到了未展平的结果,这对我来说完全没问题。但是,IAsyncResult / ReadWriteTask 的情况不同。有什么区别?

【问题讨论】:

  • Ad UPDATE1:好吧,如果你这样做,你在哪里存储输出缓冲区 (data)?您的封装完全错误,处理请求所需的所有结构都应与主代码分开。
  • 广告UPDATE2:有趣。似乎我们忽略了一些细微的差异。或者可能只是调试器中有一些特殊情况,它肯定不会是我见过的第一个。无论如何,这只是一种好奇——它不应该对正在发生的事情产生任何影响。

标签: c# asynchronous filestream iasyncresult


【解决方案1】:

IAsyncResult 只是一个接口。您可以很容易地看到在这种情况下变量的实际类型是System.IO.Stream.ReadWriteTask - 它只是实现了接口IAsyncResult。这意味着它必须拥有IAsyncResult 的属性,但它也可以拥有它想要的任何其他属性。如果将变量转换为ReadWriteTask,您还将在 IntelliSense 中看到其他属性和方法,并且可以使用它们(尽管您可能不应该使用它们)。您可能想复习一下 classinterface 是什么。

确保文件实际上是可访问的。您可能正在做一些阻止您在代码中成功打开文件的事情 - 例如在其他应用程序中打开文件。

没有理由将文件流作为状态传递。不过,它也不应该受到伤害。

另外,你为什么使用BeginRead?您正在运行.NET 4.5(由BeginRead 返回的ReadWriteTask 证明),那么为什么不简单地使用var bytesRead = await fs.ReadAsync(...);?事实上,如果使用得当,您根本不需要 ReadLine - 您应该等待您实际执行的操作完成。

当然,在这种情况下,异步 I/O 无论如何都是没有意义的,但我认为这只是一个示例,您打算用它做一些更有用的事情。

编辑

这是一个完整的工作 sn-p,不会显示您的错误:

using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AsyncFileStream
{
  class Program
  {
    static void Main(string[] args)
    {
      FileStream fs = new FileStream("d:\\temp.txt", FileMode.Open);
      int size = (int)fs.Length;

      byte[] data = new byte[size];

      var result = 
          fs.BeginRead(data, 0, size, (ar) => Callback(fs, ar, data), null);

      if (result.CompletedSynchronously) Callback(fs, result, data);

      Console.ReadLine();
    }

    static void Callback(FileStream fs, IAsyncResult ar, byte[] data)
    {
      var bytesRead = fs.EndRead(ar);

      Console.WriteLine(UTF8Encoding.UTF8.GetString(data, 0, bytesRead));
    }
  }
}

这是否显示与您的代码相同的错误?如果没有,您可能需要为您的回调方法添加代码以及一些更相关的信息。

免责声明:这仍然是从文件中读取的不好方法。不能保证一次读取就可以获取所有数据,事实上,很多时候一次读取整个文件是一种巨大的资源浪费,完全没有必要。

【讨论】:

  • 该文件是可访问的,我 100% 确定。只需按照问题中的描述移动断点,就会发生错误。对于另一个问题,如果您密切注意快速监视窗口,您会看到所有属性都被展平,这在使用我自己的界面时是不可能的。另一方面,ReadWriteTask 是在抽象 Stream 类中定义的“私有”、“密封”类。所以,这不可能。还有什么想法吗? (是的,这只是一个示例,我不会使用它)。
  • @JoeBank 您仍然对类和接口感到困惑。这些属性是“扁平化的”,因为没有基本类型IAsyncResult - 您只是看到类型Task 的属性,它恰好实现了IAsyncResult。由于继承链中没有其他类具有自己的公共属性或方法,因此您看到的是Task。当您使用自己的接口时,也会发生同样的事情。此外,ReadWriteTaskprivate sealed 的事实完全无关紧要。返回值为IAsyncResult,这很重要。它是Task 的事实是一个实现细节。
  • 好吧,我在一个干净的控制台应用程序中尝试了您的代码,但仍然发生相同的错误(如果我将断点放在 BeginRead 行)。也许调试器正在尝试做一些破坏内存的事情,我不知道。同样的事情发生在你身上吗?我正在使用 Win7(64 位)、.NET Framework 4.5、Visual Studio 2013 Update 2。
  • 我更新了那个类/接口的问题(update2)。请你检查一下好吗?我真的需要了解那里发生了什么。谢谢。
  • @JoeBank 实际上,我的配置完全相同,我没有遇到您遇到的问题。也许重新安装 Visual Studio 是为了? :D 我已经看到调试器几次出现问题(但从来没有在我的计算机上),所以我想如果它的环境出现问题,它会变得有点挑剔。你在VS中安装了任何附加组件吗? .NET Reflector 和 ReSharper 往往会经常破坏 VS :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 2020-08-29
  • 1970-01-01
  • 1970-01-01
  • 2018-01-31
  • 1970-01-01
相关资源
最近更新 更多