我正在使用WPF开发应用程序。我使用Serilog
进行日志记录,我有一个日志接收器,它在列表框中显示日志事件。传入的日志事件在日志接收器的Emit
方法中排队,排队的日志事件在CompositionTarget.Rendering
事件中处理。
排队日志事件:
public void EventsLogged(IEnumerable<Event> Events)
{
System.Windows.Application.Current?.Dispatcher.Invoke((Action)delegate
{
foreach (var item in Events)
{
_logMessageQueue.Enqueue(new LogEvent(item));
}
});
}
渲染事件:
private void CompositionTargetOnRendering(object sender, EventArgs e)
{
while (_logMessageQueue.TryDequeue(out var logEvent))
{
Messages.Add(logEvent);
}
}
Messages
是绑定到列表框的ItemSource的ObservableCollection
。
这是我的问题,当在短时间内有大量的排队日志事件时,UI响应变得可怕。有什么建议可以改进我的申请吗?或者任何我能遵循的学习材料都会很好。
谢谢你
1条答案
按热度按时间ajsxfq5m1#
以下是一些可以提高性能的建议:
Dispatcher
来收集日志事件,则应使用具有适当优先级的Dispatcher.InvokeAsync
。请注意,您应该避免在Dispatcher
中充满任务。如果您希望以高频调用Dispatcher
,请考虑使用不同的方法(例如背景线程):Dispatcher
。除非LogEvent
扩展DispatcherObject
,否则您可以在后台线程或异步执行数据结构转换(取决于您的需求):while
循环是阻塞操作。当Dispatcher
空闲时,可以通过使用具有显式优先级的Dispatcher
来执行代码来防止阻塞:为了进一步改进解决方案,我建议实现 Producer/Consumer 模式。如果使用
Channel
,则可以使整个实现异步(请参阅Microsoft文档:System.Threading.Channels library)。为了提高效率,此示例还通过处理
CompositionTarget.Rendering
事件来避免从队列中阅读。相反,该示例将在后台线程上启动一个循环以使用队列。Dispatcher.InvokeAsync
用于控制Dispatcher
上的压力:使用DispatcherPriority.Background
或DispatcherPriority.ContextIdle
应减轻压力,以便主螺纹可以继续渲染表面。最终的解决方案如下: