0

保留中のタスクのスケジュールテーブルからのポーリングとそれらの実行を管理するために、Windowsサービスの基本クラスを構築しています。

Windowsサービスは、を使用しSystem.Timers.Timerてスケジュールのテーブルポーリングを開始しています。

ThreadPool.SetMaxThreadタイマーを初期化する前に、を10に設定しています。

    protected override void OnStart(string[] args)
    {
        ThreadPool.SetMaxThreads(10, 10);

        this._Timer = new System.Timers.Timer();
        this._Timer.Elapsed += new ElapsedEventHandler(PollWrapper);
        this._Timer.Interval = 100;
        this._Timer.Enabled = true;
    }

タイマーによって呼び出されるデリゲートメソッドは、実行中のスレッドのカウントを保持するため、OnStop()メソッドで使用して、サービスを破棄する前に各スレッドが完了するのを待つことができます。

    private void PollWrapper(object sender, ElapsedEventArgs e)
    {
        numberOfRunningThreads++;

        try
        {
            this.Poll(sender, e);
        }
        catch (Exception exception)
        {
            //some error logging here
        }
        finally
        {
            numberOfRunningThreads--;
        }
    }




    protected override void OnStop()
    {
        this._Timer.Enabled = false;

        while (numberOfRunningThreads > 0)
        {
            this.RequestAdditionalTime(1000);
            Thread.Sleep(1000);
        }
    }

多くの場合、Windowsサービス管理コンソールからサービスを停止しようとしても、サービスは停止しません。デバッグしてOnStop()メソッドにブレークポイントを追加すると、numberOfRunningThreadsが0より大きい数(多くの場合10より大きい!)でスタックしているためではないことがわかります。実行中のタスクはなく、その数に永久に留まります。

ThreadPool.SetMaxThreadsまず、10に制限する必要があるにもかかわらず、その数が10を超える可能性があることを理解していませんか?

次に、スレッドの最大数を設定しなかった場合でも、PollWrapperのfinallyブロックが最終的にカウントを0に戻すことを期待します。カウンターが0より大きいままの場合、finallyブロックでは説明できません。実行中ですよね!?それはどのように可能ですか?

そして最後に、ポーリングを固定数(.NET 3.5)の可能な同時実行スレッドの数に制限する別の方法を提案しますか?

どうもありがとう。

アップデート:

再入可能性とSetMaxThreadに関するYahiaのコメントを読んだ後、PollWrapperを変更して、生成される実行中のスレッドの最大数を常に制限するようにしました。投票が再入可能であることを確認します。

private void PollWrapper(object sender, ElapsedEventArgs e)
        {
            lock(this)
            {
                if(this.numberOfRunningThreads < this.numberOfAllowedThreads)
                {
                    this.numberOfRunningThreads++;

                    Thread t = new Thread(
                        () =>
                        {
                            try
                            {
                                this.Poll(sender, e);
                            }
                            catch (Exception ex)
                            { 
                                //log exception
                            }
                            finally
                            {
                                Interlocked.Decrement(ref this.numberOfRunningThreads);
                            }
                        }
                    );
                    t.Start();
                }
            }
4

3 に答える 3

7

はい、finallyブロックが実行されない場合があります。

  • 最終的に実行する前に、コンピュータの電源を切ることができます。
  • tryブロックのコードは決して終了しない可能性があります(無限ループなど)。
  • Environment.FailFast最終的にブロックを実行しません。
  • ランタイムで重大なエラーが発生すると、finallyを実行せずにプロセス全体がクラッシュする可能性があります。

さらに、finallyブロックは実行を開始する可能性がありますが、中断された場合、例外をスローした場合、または無限ループに入った場合は完了しません。


ここでの問題は、複数のスレッドを使用しているが、共有変数へのアクセスを同期していないことのようです。

numberOfRunningThreads++;

ここで共有オブジェクトをロックする必要があります。

于 2011-09-29T13:34:08.240 に答える
5

問題は、numberOfRunningThreadsのアクセスをロックしないことです。
複数のスレッドがこれを一度に変更すると、numberOfRunningThreadsが正しくインクリメントまたはデクリメントされない競合状態が発生する可能性があります。

との代わりに、Interlocked.Incrementを使用できます。Interlocked.Decrement++..

于 2011-09-29T13:36:35.753 に答える
0

更新された質問/コードに関するOPの要求(上記のコメントを参照)に従って:

ステートメントは「クリティカル」セクションのように動作します。lockつまり、特定のコードブロックが並行して実行されるのを防ぎます。のパラメータはlock「同期オブジェクト」として使用されるため、同じ変数でで囲まれたコードブロックはlock実行されません。別のスレッドで並行して。

lockステートメントは、変数/その内容に対して何もしません。たとえば、内容を「フリーズ」しないため、実行中のコード(並列)で保護されていない限り、いつでもどこでも変更できます。lockパラメータと同じ変数。

私が提供したリンク(VS2003の古いドキュメントバージョンへのポイント)を見ると、たとえばlock(this)は使用できません-理由とベストプラクティスがそれに応じて説明されています...

上記のすべては、提供されたコード(更新されたバージョン)をまだスレッドセーフではありません(ただし、元のバージョンよりも明らかに優れています)。

于 2011-10-03T16:46:05.880 に答える