7

シングルトンのロガークラスがあります。そのデストラクタで、ログのフッターを出力してから StreamWriter を閉じる Close() を呼び出します。

 public void Close()
    {
        WriteLogFileFooter();

        _logFile.Flush();

        _logFile.Close();
    }

問題は、 System.Enviornment.Exit(1) がプログラムの他の場所から呼び出された場合 (自分で書いていない部分)、フッターが出力されず、ロガーが閉じたストリームに書き込もうとして例外をスローすることです。シングルトンが破棄される前に、Exit コマンドが StreamWriter を閉じているとしか思えません。StreamWriter で GC.SupressFinalize() を使用しようとしましたが、役に立たなかったようです。

4

5 に答える 5

13

ファイナライザーの1つの明示的なルールに違反しています。

Finalizeメソッドは、他のオブジェクトを参照しないでください。

http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=VS.90).aspx

アプリケーションの終了時にオブジェクトが収集される前に、参照を保持している管理対象オブジェクトが収集される可能性は十分にあります。

アップデート

アプリケーションの終了時に管理対象リソースをクリーンアップする必要がある場合は、ファイナライザー実行の非決定論的な動作に依存するのではなく、AppDomainのProcessExitイベントをフックすることができます。

.NETコンソールアプリケーション終了イベント

于 2012-07-05T17:03:23.380 に答える
3

ロガーを実装IDisposableし、usingブロックで使用する必要があります。これは、決定論的に破棄されることを意味しますが、現在は非決定論的に破壊されています。

エラーの原因は、Exit基本的にすべてを (非決定論的に) 破棄して終了するため、ロガーの前にストリームが閉じられることがあるということです。IDisposableこれを回避するには、決定論的パターン ( ) を使用する必要があります。

実際には、デストラクタが非決定論的であるという正確な理由から、C# でデストラクタが役立つことはめったにありません。管理されていないリソースを解放するためにのみ使用する価値があります。

また、実装IDisposableすると、シングルトンの使用が不便になる場合があります。個人的には、明示的なシングルトンではなく、プログラム全体で使用され、最後に破棄されるインスタンスを作成する方が良いと思います。

于 2012-07-05T16:59:10.070 に答える
1

他の人がすでに明確に述べているように、_logFileロガークラスのファイナライザーからオブジェクトにアクセスしようとしないでください。ガベージコレクターが既にオブジェクトを消去している可能性があるため、ファイナライザー内の他のオブジェクトにアクセスしないでください。

いくつかの簡単な手順で問題を回避できると思います。

  1. 現在のファイナライザーを取り除きます。

  2. _logFile.Flushすでに手遅れになっている可能性があるロガーオブジェクトの存続期間の最後まで待つのではなく、すべての書き込みの後に実行します。

    ログファイルストリームをフラッシュすることは、エラーが発生した状況を見つけて処理するためにログを使用することであるため、私には頻繁に正当に思えます。例外的な状況によってプロセスが突然終了した場合でも、ログは可能な限り完全である必要があります。したがって、ログストリームバッファをフラッシュすることは、頻繁に行うのが賢明なことのように思われます。

  3. ロガーを実装しIDisposableこのMSDN Magazineの記事でこれがどのように行われるかを説明します)、そこからログファイルストリームを閉じます。

于 2012-07-05T17:38:58.077 に答える
0

私は同じ問題を抱えていましたが、私の解決策は次のとおりです。

  1. クラスFileStreamコンストラクターでを作成すると、GC.SuppressFinalizeすぐに使用されます。これにより、ストリームのクリーニングを担当するようになります
  2. Dispose()クラスの でストリームを閉じます
public class LogFileEventListener : IDisposable
{
    private bool disposed = false;
    private FileStream fileStream;

    public LogFileEventListener(string path)
    {
        //Opens a new file stream to log file
        this.fileStream = new FileStream(path, FileMode.Append, FileAccess.Write);
        GC.SuppressFinalize(this.fileStream);
    }

    /// <summary>Finalize the listener</summary>
    ~LogFileEventListener() { this.Dispose(); }

    /// <summary>Disposes the listener</summary>
    public override void Dispose()
    {
        try
        {
            if (!this.disposed)
            {
                /* Do you stuff */

                //Close the log file
                if (this.fileStream != null)
                {
                    this.fileStream.Close();
                    this.fileStream = null;
                }

                base.Dispose();
            }
        }
        finally
        {
            this.disposed = true;
            GC.SuppressFinalize(this);
        }
    }
}
于 2013-02-18T07:53:29.167 に答える
-2

ほとんどの場合、StreamWriter他の場所で閉鎖されています。シングルトンのコンストラクターで追加StreamWriterを作成し、(それが機能していることを確認するために) 数回書き込みを行ってから、close を呼び出す前にデストラクタで再度書き込みます (close もフラッシュします)。

上記が機能する場合、他のコードがログを閉じていることがわかります。それが機能しない場合は、それが .NET のものであることがわかります (変数がどのように/どこで参照されているかに関係している可能性があります)。

documentationによると、StreamWriterを基本クラスに入れることで問題を回避できるはずです。テストケースは標準のファイナライズではなく、プログラムの終了であるため、これはもちろん機能しません。つまり、.NETは必要なときに必要なことを行います。代わりに、終了イベントをキャッチし、このクラスを破棄してから戻る必要があります。これにより、物事が正しい順序で破棄されることが保証されます。StreamWriterエラーが原因でプログラムが中止された場合に備えて、ファイナライザーで が既に閉じられているかどうかも確認する必要があります。

于 2012-07-05T17:01:43.047 に答える