我的应用程序中有一个日志窗口,当我有几千个日志时,过滤它们以包含或排除不同的日志级别会使UI在工作负载的一段时间内无响应。所以我试图将繁重的工作转移到工作线程,并且仍然遇到同样的问题。
我使用ObservableCollection
来保存模型中的日志信息,我只是直接用我的ViewModel引用它。我使用BindingOperations.EnableCollectionSynchronization()
让我的工作线程更新我的可观察集合而不将其调度到UI线程。
我运行以下内容以使用
更新工作线程上的集合Task.Run(new Action(() => FilterList()));
方法:
private void FilterList()
{
//Necessary even with EnableCollectionSynchronization
App.Current.Dispatcher.Invoke(new Action(() =>
{
FilteredLogEvents.Clear();
}));
foreach (LogEvent log in FilterLogEvents())
{
FilteredLogEvents.Add(log);
}
RaisePropertyChanged("FilteredLogEvents");
FinishedFilteringLogs();
}
//Filters and returns a list of filtered log events
private List<LogEvent> FilterLogEvents()
{
List<LogEvent> selectedEvents = (from x in LogEvents
where ((ViewDebugLogs == true) ? x.Level == "Debug" : false)
|| ((ViewErrorLogs == true) ? x.Level == "Error" : false)
|| ((ViewInfoLogs == true) ? x.Level == "Info" : false)
select x).ToList();
return selectedEvents;
}
这会导致UI冻结foreach
。我还尝试新增ObservableCollection
,然后使用FilteredLogEvents
为FilteredLogEvents = myNewCollection;
分配Thread.Sleep(1)
,这也会导致UI在此过程中暂停一段时间。
如果我在foreach
循环中使用LogEntries
,UI仍会保持响应,尽管这似乎是一个不优雅和hacky的解决方案。
我需要做些什么来完成这项工作?
编辑:此类中的更多代码上下文(FinishedFilteringLogsEventHandler
)
//Constructor
public LogEntries()
{
foreach(NlogViewerTarget target in NLog.LogManager.Configuration.AllTargets.Where(t=>t is NlogViewerTarget).Cast<NlogViewerTarget>())
{
target.RecieveLog += RecieveLog;
}
FilteredLogEvents = new ObservableCollection<LogEvent>();
BindingOperations.EnableCollectionSynchronization(FilteredLogEvents, filteredLogEventsLock);
}
public delegate void FinishedFilteringLogsEvent();
public FinishedFilteringLogsEvent FinishedFilteringLogsEventHandler;
private object filteredLogEventsLock = new object();
public ObservableCollection<LogEvent> FilteredLogEvents { get; set; }
的回调返回到ViewModel,以更改bool,在过滤完成时启用两个复选框。
UIPanGentureRecognizer
答案 0 :(得分:1)
要考虑提高代码的速度和响应能力的一些想法
前几天我问了一个类似的问题,有人建议我不要使用线程池来完成长时间运行的任务。线程池是可用线程的集合,与启动System.ComponentModel.BackGroundWorker等传统线程相比,可以快速启动。
虽然创建和启动实际线程需要更多时间,但对于长时间运行的任务来说这不是问题。
线程池中的线程数量是有限的,因此最好不要将它用于更长时间运行的任务。
如果您运行任务,它只计划在线程可用时在不久的将来运行。如果所有线程都忙,则在线程启动之前需要一些时间。
从任务到背景工作者的变化是有限的。如果你真的想坚持任务,可以考虑创建一个异步函数:
async void FilteredLogEvents.AddRangeAsync(IEnumerable<LogEvent> logEvents)
或者更好:
async void FilteredLogEvents.SetAsync(IEnumerable<LogEvent> logEvents)
执行清除并添加一个异步调用。
让你自己的函数异步:
private async void FilterList()
{
var filteredLogEvents = FilterLogEvents();
var myTask = Task.Run( () => FilteredLogEvents.SetAsync(filteredLogEvents);
// if desired do other things.
// wait until ready:
await myTask();
RaisePropertyChanged("FilteredLogEvents");
FinishedFilteringLogs();
}
顺便说一下:在筛选时,你确定你的logEvents序列没有改变吗?