O retorno de chamada inicial do IncrementingPollingCounter é assíncrono
IncrementingPollingCounter usa um retorno de chamada para recuperar valores atuais de uma métrica e relata-lo por meio EventSource de eventos. No passado, a primeira invocação do retorno de chamada poderia ter ocorrido de forma síncrona em qualquer thread que estivesse habilitando o EventSource
; invocações futuras ocorreram em um thread de temporizador dedicado. A partir do .NET 9, o primeiro retorno de chamada sempre ocorre de forma assíncrona no thread do timer. Isso pode fazer com que as alterações do contador que ocorreram logo após a ativação do contador não sejam observadas porque o primeiro retorno de chamada acontece mais tarde.
É mais provável que essa alteração afete os testes que usam EventListener para validar um IncrementingPollingCounter
arquivo . Se os testes habilitarem o contador e, em seguida, modificarem imediatamente o estado que está sendo sondado pelo contador, essa modificação poderá ocorrer antes da primeira vez que o retorno de chamada for invocado (e passar despercebido).
Comportamento anterior
Anteriormente, quando um IncrementingPollingCounter
estava habilitado, a primeira invocação do retorno de chamada poderia ter ocorrido de forma síncrona no thread que executou a operação de habilitação.
Este aplicativo de exemplo chama o delegado () => SomeInterestingValue
no thread dentro da chamada para EnableEvents()
.Main
Esse retorno de chamada será 0 log.SomeInterestingValue
. Uma chamada posterior de um thread de temporizador dedicado observará log.SomeInterestingValue
alterado para 1, e um evento será enviado com 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"]}");
}
}
}
Novo comportamento
Usando o mesmo trecho de código da seção Comportamento anterior, a primeira invocação do retorno de chamada ocorre de forma assíncrona no thread do temporizador. Isso pode ou não ocorrer antes da execução log.SomeInterestingValue++
do Main
thread, dependendo de como o sistema operacional agenda vários threads.
Dependendo desse tempo, o aplicativo produz "Increment=0" ou "Increment=1".
Versão introduzida
.NET 9 RC 1
Tipo de mudança de rutura
Esta mudança é uma mudança comportamental.
Razão para a alteração
A alteração foi feita para resolver um possível impasse que pode ocorrer executando funções de retorno de chamada enquanto o EventListener
bloqueio é mantido.
Ação recomendada
Nenhuma ação é necessária para cenários que usam IncrementingPollingCounters
para visualizar métricas em ferramentas de monitoramento externas. Estes cenários devem continuar a funcionar normalmente.
Para cenários que fazem testes em processo ou outro consumo de dados do contador via EventListener
, verifique se seu código espera observar uma modificação específica no valor do contador feita no mesmo thread chamado EnableEvents()
. Se isso acontecer, recomendamos esperar para observar pelo menos um evento de contador do EventListener
, em seguida, modificando o valor do contador. Por exemplo, para garantir que o trecho de código de exemplo imprima "Increment=1", você pode adicionar um ManualResetEvent
ao EventListener
, sinalizá-lo quando o primeiro evento de contador for recebido e aguardar por ele antes de chamar log.SomeInterestingValue++
.