Udostępnij za pośrednictwem


IncrementingPollingCounter początkowe wywołanie zwrotne jest asynchroniczne

IncrementingPollingCounter używa wywołania zwrotnego, aby pobrać bieżące wartości metryki i zgłosić je za pośrednictwem EventSource zdarzeń. W przeszłości pierwsze wywołanie wywołania zwrotnego mogło mieć miejsce synchronicznie na dowolnym wątku, który włączał EventSource; przyszłe wywołania miały miejsce w dedykowanym wątku czasomierza. Począwszy od platformy .NET 9, pierwsze wywołanie zwrotne zawsze występuje asynchronicznie w wątku czasomierza. Może to spowodować zmiany licznika, które wystąpiły tuż po włączeniu licznika będzie nieobserwowane, ponieważ pierwsze wywołanie zwrotne nastąpi później.

Ta zmiana najprawdopodobniej wpłynie na testy, które używają EventListener do weryfikowania elementu IncrementingPollingCounter. Jeśli testy włączą licznik, a następnie natychmiast zmodyfikuj stan sondowany przez licznik, ta modyfikacja może teraz wystąpić przed pierwszym wywołaniem wywołania zwrotnego (i przejdź do niezauważonego).

Poprzednie zachowanie

Wcześniej, gdy włączono funkcję IncrementingPollingCounter , pierwsze wywołanie wywołania zwrotnego mogło wystąpić synchronicznie w wątku, który wykonał operację włączania.

Ta przykładowa aplikacja wywołuje delegata () => SomeInterestingValue w wątku Main w wywołaniu metody EnableEvents(). To wywołanie zwrotne będzie obserwowane log.SomeInterestingValue jest 0. Późniejsze wywołanie z dedykowanego wątku czasomierza będzie obserwować log.SomeInterestingValue zmianę na 1, a zdarzenie zostanie wysłane za pomocą polecenia 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"]}");
        }
    }
}

Nowe zachowanie

Używając tego samego fragmentu kodu co w sekcji Poprzednie zachowanie , pierwsze wywołanie wywołania zwrotnego odbywa się asynchronicznie w wątku czasomierza. Może to wystąpić lub nie występuje przed uruchomieniem log.SomeInterestingValue++ wątku Main w zależności od tego, jak system operacyjny planuje wiele wątków.

W zależności od tego czasu aplikacja generuje dane wyjściowe "Increment=0" lub "Increment=1".

Wprowadzona wersja

.NET 9 RC 1

Typ zmiany powodującej niezgodność

Ta zmiana jest zmianą behawioralną.

Przyczyna wprowadzenia zmiany

Wprowadzono zmianę w celu rozwiązania potencjalnego zakleszczenia, które mogą wystąpić podczas uruchamiania EventListener funkcji wywołania zwrotnego podczas blokowania.

W scenariuszach używanych IncrementingPollingCounters do wizualizacji metryk w narzędziach do monitorowania zewnętrznego nie jest wymagana żadna akcja. Te scenariusze powinny nadal działać normalnie.

W przypadku scenariuszy, które przeprowadzają testowanie w procesie lub inne użycie danych licznika za pośrednictwem metody EventListener, sprawdź, czy kod oczekuje obserwowania określonej modyfikacji wartości licznika wykonanej w tym samym wątku, który nazwał EnableEvents(). Jeśli tak, zalecamy oczekiwanie na obserwowanie co najmniej jednego zdarzenia licznika EventListenerz klasy , a następnie zmodyfikowanie wartości licznika. Aby na przykład upewnić się, że przykładowy fragment kodu drukuje wartość "Increment=1", można dodać element ManualResetEvent do elementu , zasygnalizować go po odebraniu EventListenerpierwszego zdarzenia licznika i zaczekać na nie przed wywołaniem metody log.SomeInterestingValue++.

Dotyczy interfejsów API