3

最近使用するように変更した次のコードを検討してください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
);

Windows API呼び出しが返さGC.KeepAlive(fs)れるまで FileStream がガベージ コレクションされないようにするために、以前に を追加していました。WriteFile()

ただし、SafeFileHandleコード分析を使用するように変更した後、次の場合は不要であることがわかりましたwarning CA2004: Remove calls to GC.KeepAlive

SafeHandle の使用法に変換する場合は、GC.KeepAlive (オブジェクト) へのすべての呼び出しを削除します。

のドキュメントを参照しましたFileStream.SafeFileHandleが、 への呼び出しを削除しても本当に安全かどうかは不明ですGC.KeepAlive()

削除しても安全ですか?そして、私はそれを正しく使用していますか?

また、SafeHandle の使用に関する適切なドキュメントを教えてくれる人はいますか?

4

1 に答える 1

4

SafeHandle を使用するポイントは、WriteFile() 関数の実行中にハンドルが閉じられないことです。ここで本当に達成したいことです。ただし、FileStream オブジェクトはまだファイナライズされている可能性があることに注意してください。投稿されたコードで発生したことによる明らかな結果はありません。したがって、FxCop の警告は適切です。

このようなコードを使用することに付随する文字列に注意してください。FileStream.Write() よりも高速になる可能性はほとんどありません。ただし、エッジ条件を適切に処理しないと、リスクが増大します。Overlapped の使用を含め、重複した I/O を適切に処理しない場合は、それを行わないでください。重複した I/O が実際に意図されている場合は、GCHandle が実行できる範囲を超えて CLR で最適化されている方法について、この回答を確認してください。FileStream のリファレンス ソースソース コードをよく見てください。特に、_isAsync フィールドと、ERROR_NO_DATA および ERROR_INVALID_HANDLE のエラー処理に注目してください。

また、FxCop の要求のように、GC.KeepAlive() を使用せに SafeFileHandle を使用していることもわかります。

于 2013-04-30T10:40:39.700 に答える