[英]Reporting/logging from with in a Task
我有一个异步调用,如果给定记录处理失败,该调用将引发异常。 该异常被捕获为聚合异常。 现在,有一个要求,我必须从异步方法中记录警告消息。 我无法登录到TextBox / Grid,因为它不允许访问不同线程上的控件,并且由于我实际上想登录并继续执行该任务而无法引发异常。 这是启动任务的父代码:
private List<OrganizationUser> GenerateDataList(string importFilePath, int lastUserId = -1)
{
var resultList = new List<OrganizationUser>();
// Note: ReadLine is faster than ReadAllLines
var lineCount = File.ReadLines(importFilePath).Count();
LogMessage(new LogEntry(String.Format(" - File has {0} lines.", lineCount)));
var consistancyCounter = 0;
ResetProgress();
using (var fs = File.Open(importFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var bs = new BufferedStream(fs))
{
using (var sr = new StreamReader(bs))
{
string readLine;
while ((readLine = sr.ReadLine()) != null)
{
if (string.IsNullOrEmpty(readLine) || readLine == "---EOF---")
{
break;
}
try
{
var processLineTask = Task.Run(() => GenerateDataListInternal(nextId++, localReadLine, localConsistancyCounter));
processLineTask.Wait();
var result = processLineTask.Result;
if (result != null)
{
resultList.Add(result);
}
}
catch (AggregateException exp)
{
if (exp.InnerExceptions.Count == 1 && exp.InnerExceptions.Any(x => x is DataFileBadColumnNumbers || x is DataFileGenerateListException))
{
LogMessage(new LogEntry(exp.InnerExceptions[0].Message, LogEntryType.Warning));
}
else if (exp.InnerExceptions.Count == 1 && exp.InnerExceptions.Any(x => x is IndexOutOfRangeException))
{
LogMessage(new LogEntry(String.Format(" - Data cannot be parsed at line #{0}. Data is: {1}", localConsistancyCounter + 1, localReadLine), LogEntryType.Warning));
}
else
{
throw;
}
}
}
if (ProgressBarImport.Value <= ProgressBarImport.Maximum)
{
ProgressBarImport.PerformStep();
}
}
}
}
}
在上面的代码中, GenerateDataListInternal
是引发异常并且现在需要记录的方法。
如何从GenerateDataListInternal
方法中登录? 我已经尝试了委托方法,并且只是挂了应用程序。 我有一个方法登录到控制台,网格和文本文件(按此顺序)。 由于跨线程操作,从异步方法对该方法的任何调用都会失败。
这已经通过System.IProgress接口和Progress类提供了,其中T
可以是任何类,从而使您报告的不仅仅是简单的进度百分比。
IProgress<T>.Report
允许您从异步操作内部报告进度(或其他任何东西),而不必担心谁会真正处理该报告。
每当您的任务调用.Report
时, Progress<T>
都会在创建它的线程(例如UI线程)上调用操作和/或引发事件。
.NET Framework Blog中的以下示例显示了使用IProgress<T>
多么容易:
async Task<int> UploadPicturesAsync(List<Image> imageList, IProgress<int> progress)
{
int totalCount = imageList.Count;
int processCount = await Task.Run<int>(() =>
{
int tempCount = 0;
foreach (var image in imageList)
{
//await the processing and uploading logic here
int processed = await UploadAndProcessAsync(image);
if (progress != null)
{
progress.Report((tempCount * 100 / totalCount));
}
tempCount++;
}
return tempCount;
});
return processCount;
}
异步过程从以下代码开始:
private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
//construct Progress<T>, passing ReportProgress as the Action<T>
var progressIndicator = new Progress<int>(ReportProgress);
//call async method
int uploads=await UploadPicturesAsync(GenerateTestImages(), progressIndicator);
}
请注意, progressIndicator
是在UI线程中创建的,因此ReportProgress
方法将在UI线程上调用。
更新
从注释中听起来,您似乎正在尝试创建自己的日志记录解决方案,并且在从多个线程访问日志文件时遇到问题。
最好的解决方案是使用日志库,例如log4net
, NLog
或什至.NET的诊断类。 它们都可以在多个线程上正常工作。
IProgress<T>
在这里仍然可以提供帮助,因为处理Report事件的委托可以将消息简单地写入到您已经在UI线程上创建的日志文件中。
您可以这样写:
var progress = new Progress<LogEntry>(entry =>
{
_logFile.WriteLine("{0} : {1}",entry.EntryType,entry.Message);
});
并从任务内部报告:
var entry=new LogEntry(...);
progress.Report(entry);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.