英語で読む

次の方法で共有


IncrementingPollingCounterの初期コールバックは非同期です

IncrementingPollingCounter はコールバックを使用してメトリックの現在の値を取得し、 EventSource イベントを介してそれを報告します。 これまでは、コールバックの最初の呼び出しは、 EventSourceを有効にしているスレッドで同期的に発生していましたが、それ以降の呼び出しは専用のタイマー スレッドで発生しました。 .NET 9 以降では、最初のコールバックは常にタイマー スレッドで非同期的に発生します。 これにより、最初のコールバックが後で発生するため、カウンターが有効になった直後に発生したカウンターの変更が監視されなくなる可能性があります。

この変更は、EventListener を使用して IncrementingPollingCounterを検証するテストに影響を与える可能性が最も高くなります。 テストでカウンターを有効にし、その後すぐにカウンターによってポーリングされている状態を変更すると、その変更はコールバックが最初に呼び出される前に発生する可能性があります (そして気付かれません)。

以前の動作

以前は、 IncrementingPollingCounter が有効になっている場合、コールバックの最初の呼び出しは、有効化操作を実行したスレッドで同期的に発生する可能性がありました。

このサンプル アプリは、EnableEvents()の呼び出し内で Main スレッド上のデリゲート () => SomeInterestingValue を呼び出します。 そのコールバックは log.SomeInterestingValue が 0 であることを確認します。 専用のタイマー スレッドからの後の呼び出しでは、 log.SomeInterestingValue が 1 に変更され、 Increment value = 1とともにイベントが送信されます。

using System.Diagnostics.Tracing;

var log = MyEventSource.Log;
using var listener = new Listener();

log.SomeInterestingValue++;

Console.ReadKey();

class MyEventSource : EventSource
{
    public static MyEventSource Log { get; } = new();
    private IncrementingPollingCounter? _counter;
    public int SomeInterestingValue;

    private MyEventSource() : base(nameof(MyEventSource))
    {
        _counter = new IncrementingPollingCounter("counter", this, () => SomeInterestingValue);
    }
}

class Listener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        if (eventSource.Name == nameof(MyEventSource))
        {
            EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None,
                new Dictionary<string, string?> { { "EventCounterIntervalSec", "1.0" } });
        }
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (eventData.EventSource.Name == "EventCounters")
        {
            var counters = (IDictionary<string, object>)eventData.Payload![0]!;
            Console.WriteLine($"Increment: {counters["Increment"]}");
        }
    }
}

新しい動作

以前の動作 セクションと同じコード スニペットを使用すると、コールバックの最初の呼び出しはタイマー スレッドで非同期的に発生します。 OS が複数のスレッドをスケジュールする方法に応じて、 Main スレッドが log.SomeInterestingValue++ を実行する前に発生する場合と発生しない場合があります。

そのタイミングに応じて、アプリは「Increment=0」または「Increment=1」を出力します。

導入されたバージョン

.NET 9 RC 1

破壊的変更の種類

この変更は、動作変更です。

変更理由

この変更は、 EventListener ロックが保持されている間にコールバック関数を実行するときに発生する可能性のあるデッドロックを解決するために行われました。

IncrementingPollingCounters を使用して外部監視ツールでメトリックを視覚化するシナリオでは、アクションは必要ありません。 これらのシナリオは引き続き正常に動作するはずです。

EventListener を介してインプロセス テストまたはカウンター データのその他の消費を行うシナリオでは、コードが EnableEvents()を呼び出した同じスレッドで行われたカウンター値への特定の変更を観察することを想定しているかどうかを確認します。 そうなる場合は、 EventListenerから少なくとも 1 つのカウンター イベントが観察されるまで待機してから、カウンター値を変更することをお勧めします。 たとえば、 サンプル コード スニペット が「Increment=1」を出力するようにするには、 EventListenerManualResetEvent を追加し、最初のカウンター イベントを受信したときにそれを通知し、 log.SomeInterestingValue++を呼び出す前にそれを待機します。

影響を受ける API