【问题标题】:After using FileStream.SafeFileHandle, is it safe for me to remove a call to GC.KeepAlive()?使用 FileStream.SafeFileHandle 后,删除对 GC.KeepAlive() 的调用是否安全?
【发布时间】:2013-04-30 09:49:42
【问题描述】:

考虑以下我最近更改为使用FileStream.SafeFileHandle 的代码:

public static void FastWrite<T>(FileStream fs, T[] array, int offset, int count) where T: struct
{
    int sizeOfT = Marshal.SizeOf(typeof(T));
    GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);

    try
    {
        uint bytesWritten;
        uint bytesToWrite = (uint)(count * sizeOfT);
        var overlapped = new NativeOverlapped();

        if
        (
            !WriteFile
            (
                fs.SafeFileHandle,
                new IntPtr(gcHandle.AddrOfPinnedObject().ToInt64() + (offset*sizeOfT)),
                bytesToWrite,
                out bytesWritten,
                ref overlapped
            )
        )
        {
            throw new IOException("Unable to write file.", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        Debug.Assert(bytesWritten == bytesToWrite);

        GC.KeepAlive(fs); // <--- Is this really not necessary?
    }

    finally
    {
        gcHandle.Free();
    }
}

[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]

private static extern bool WriteFile
(
    SafeFileHandle       hFile,
    IntPtr               lpBuffer,
    uint                 nNumberOfBytesToWrite,
    out uint             lpNumberOfBytesWritten,
    ref NativeOverlapped lpOverlapped
);

我之前添加了 GC.KeepAlive(fs) 以确保在 Windows API WriteFile() 调用返回之前不会对 FileStream 进行垃圾收集。

但是,在更改为使用 SafeFileHandle 之后,代码分析现在告诉我,warning CA2004: Remove calls to GC.KeepAlive 没有必要:

如果您要转换为 SafeHandle 用法,请删除对 GC.KeepAlive(对象)的所有调用。

我已经查阅了FileStream.SafeFileHandle 上的文档,但我不清楚删除对GC.KeepAlive() 的调用是否真的安全。

删除它绝对安全吗?我是否正确使用它?

另外,任何人都可以指出一些关于使用 SafeHandle 的体面文档吗?

【问题讨论】:

  • @Damien_The_Unbeliever 呵呵,感谢您的编辑。它也在标题中!

标签: c# filestream safehandle


【解决方案1】:

使用 SafeHandle 的意义在于,在 WriteFile() 函数执行时,handle 不会被关闭。这确实是您想要在这里实现的目标。但是请注意,FileStream 对象可能仍然是最终确定的。发布的代码中没有明显的后果。所以 FxCop 警告是合适的。

请注意使用这样的代码附加的字符串。它不太可能比 FileStream.Write() 快。但是你确实会因为没有正确处理边缘条件而增加风险。包括使用 Overlapped 但未适当处理重叠 I/O,不要那样做。如果实际打算使用重叠 I/O,则检查 this answer 以了解在 CLR 中优化的方式超出了 GCHandle 的能力。仔细查看 FileStream 的 Reference Source 源代码,特别关注 _isAsync 字段以及 ERROR_NO_DATA 和 ERROR_INVALID_HANDLE 的错误处理。

您还会看到它使用 SafeFileHandle 而使用 GC.KeepAlive(),就像 FxCop 要求的那样。

【讨论】:

  • 谢谢汉斯。这段代码最初是几年前编写的,目的是帮助我们与遗留的 C++ 代码进行互操作,因此它更关注的是速度而不是速度。但是,它似乎比使用BinaryWriter 快得多(事实上快得多,我认为我一定做错了什么,所以我要提出一个新问题) .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-21
  • 2010-10-07
相关资源
最近更新 更多