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
的一個計數器事件,然後修改計數器值。 例如,若要確保 範例程式碼片段列印 "Increment=1",您可以將 ManualResetEvent
新增至 EventListener
,在收到第一個計數器事件時發出訊號,並在呼叫 log.SomeInterestingValue++
之前等候它。