2

90 日前のメールを毎晩アーカイブする必要がある機能があります。これを処理するクラスを作成しました。例を次に示します。

    public void processArchives()
    {
        initializeTimer();
    }

    private void initializeTimer()
    {
        var now = DateTime.Now;
        var tomorrow = now.AddDays(1);
        var durationUntilMidnight = tomorrow.Date - now;

        var t = new Timer(o => { attemptArchivalProcess(); }, null, TimeSpan.Zero, durationUntilMidnight);
    }

    private void attemptArchivalProcess()
    {
        //perform archival
        initializeTimer(); //re-start timer to process tomorrow
    }

問題は、initializeTimer を繰り返し呼び出すと、スタック オーバーフロー (関数呼び出しの繰り返し) が発生するか、それとも「永久に」正常に実行されるかということです。

processArchives() を新しいスレッドとして呼び出し、スレッドを開いたままにするか、init 呼び出しの後に次のようなループが必要ですか。

    while(!Program.Closing){ sleep(...); }

ガベージコレクションされないようにするには?

4

3 に答える 3

1

私はあなたがあなたの潜在的な解決策に近いと思います。

タイマー

最初の質問に答えるには:すでに結論を出しているので、タイマーはその代理人で経過します。デリゲートは個別のスレッドで実行され、タイマーが経過するたびに、実行する新しい新しい独自のスタックが取得されます。したがって、無限のタイマー経過イベントがをトリガーすることはありませんStackOverflowException

永遠に待つ?

2番目の質問に答える試み:アプリケーションを存続させるために無限ループを作成する必要はありませんしかし、それは可能です。それはすべて、アプリケーションに必要なものによって異なります。長所と短所を比較検討します。

幸いなことに、より多くの解決策が可能です(正しいか間違っているかはありません。ニーズを満たすためにそれらを比較検討してください)

あなたが考えることができる解決策のショットリスト:

Console.ReadLine()

コンソールアプリケーションがある場合は、ユーザー入力を待つだけです。メインスレッドは、プロセッサの電力を消費することなく、永久に待機します。

Servyが提案したように、スケジュールされたタスクを作成します

そうすれば、無限ループを作成するために何もする必要がありません。アプリケーションは、完了すると終了します。このアプリを実際にユーザーにデプロイする場合、最も適切なソリューションではない可能性があります。

Windowsサービス

また、もう少し成熟したソリューションを選択して、Windowsサービスを作成することもできます(実際よりも複雑に聞こえますが、基本的なWindowsサービスを作成するのは非常に簡単です)。そうすれば、終わりのないループを書くことを気にする必要もありません。Windowsサービスは設計上永久に実行されます(もちろん、停止することにしたユニット)。

whileループを終わらせないための代替手段-WaitHandle

また、シグナルメカニズム(たとえば、を使用AutoResetEvent)を使用して、メインスレッドが特定のシグナルが設定されるまで待機できるようにすることもできます。そうすれば、積極的に待つ必要もありません(=プロセッササイクルを消費しません)。

あなたにはたくさんの選択肢があります、それはすべてあなたの特定のニーズに要約されます、それは私があなたのために決めることができません。あなたはできる。:)


これらすべての言葉、例を挙げましょう。単体テストはアプリケーションを表します。タイマーは別のタイプ、つまりSystem.Timers.Timer。そのタイマーをに設定してAutoReset、新しいタイマーを作成する必要がないようにすることができます。

ここでの例は、あなたにとって意味があることを願っています(そうでない場合は、コメントしてください、多分私は明確にすることができます)

    private Timer _processTimer;
    private AutoResetEvent _resetSignal;

    [Test]
    public void YourImaginaryMainApp()
    {
        const int interval = 24 * 60 * 60 * 1000; // every day

        _resetSignal = new AutoResetEvent(false);
        _processTimer = new Timer(interval)
            {
                AutoReset = true
            };
        _processTimer.Elapsed += ProcessTimerOnElapsed;

        _resetSignal.WaitOne( /*infinite*/);
    }
于 2013-02-19T21:24:17.880 に答える
1

編集 x1 - 2 番目の文の「スタック」ではなく「ヒープ」を意味します... (d'oh!)

非常に単純な理由で、これがスタック オーバーフローを引き起こすとは思いません。行var t = new Timer(... は、ヒープ上に新しいオブジェクトを作成します。関数ポインターはオブジェクト内に内部的に保持され、(理論上) 実際に呼び出されるまでスタックに追加されるべきではありません。がattemptArchivalProcess()呼び出されると、順番に呼び出しますinitializeTimer()(スタックに追加します) が、これは完了し、同じスレッドで通常どおり終了します (スタックから削除します)。がTimer起動すると、スタックへの 2 コール エントリから開始されます。

さて、これはすべて、スタックのフードの下で複雑さが増していることは知っていますが、私のポイントは、最終的には2つのメソッドが呼び出され、それらが正しく終了し、終了時に正しくクリーンアップされる必要があるということです。

または、少なくともそれが私の推論です。私はこれについて修正する余地があることを完全に認めます...

于 2013-02-19T20:45:57.373 に答える
0

これが私の試みた解決策です...

    System.Threading.Timer timerFunc = null;

    public void processArchives()
    {
        initializeTimer();

        while (!CSEmailQueues.StoppingService) //is
            Thread.Sleep(CSEmailQueues.sleeptime); 

        timerFunc.Dispose();

        return;
    }

    private void initializeTimer()
    {
        var now = DateTime.Now;
        var tomorrow = now.AddDays(1);
        var durationUntilMidnight = tomorrow.Date - now;

        if (timerFunc != null) timerFunc.Dispose();
        timerFunc = new System.Threading.Timer(o => { attemptArchivalProcess(); }, null, TimeSpan.Zero, durationUntilMidnight);
    }

    private void attemptArchivalProcess()
    {
        //Do Work
        initializeTimer(); //re-start timer to process tomorrow
    }

つまり...これにより、タイマーオブジェクトが破棄され、実行されるたびに新しいオブジェクトが作成されます(このタイマーは1回しか実行されないため)。さらに、クラスレベルの変数であるため、タイマーへの参照が常に存在するため、ガベージコレクターは、タイマーがトリガーされるのを待っている間、タイマーを破棄しません。

次に、サービスのonStart呼び出しからprocessArchives()を呼び出すスレッドを作成するだけです。これは、onStopが呼び出されてStoppingServiceがtrueに設定されない限り、本質的には永久に実行されるはずです。

また、このオブジェクトにアクセスするために常に複数のインスタンスが存在することはないため、タイマーコールバックによるtimerFuncのクロススレッド使用について心配する必要はないと思います。

于 2013-02-19T21:32:59.093 に答える