从任务中的with报告/记录

本文关键字:报告 记录 with 任务 | 更新日期: 2023-09-27 18:02:22

我有一个异步调用,如果给定的记录处理失败,则抛出异常。该异常被捕获为聚合异常。现在,有一个要求,我必须从我的异步方法记录一个警告消息。我不能登录到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方法中登录?我尝试过委托方法,它只是挂起了应用程序。我有一个方法,记录控制台,网格和文本文件(按此顺序)。由于交叉线程操作,异步方法对该方法的任何调用都将失败。

从任务中的with报告/记录

这已经通过系统提供了。IProgress接口和Progress类,其中T可以是任何类,允许您报告的不仅仅是简单的进度百分比。

IProgress<T>.Report允许您从异步操作内部报告进度(或其他任何内容),而不必担心谁将实际处理报告。

Progress<T>将在创建它的线程上调用一个动作和/或引发一个事件。(UI线程)每当你的任务调用.Report

这个来自。net Framework博客的例子展示了使用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线程中调用。

从评论中,听起来你正在尝试创建自己的日志解决方案,并且在从多个线程访问日志文件时遇到问题。

最好的解决方案是使用像log4netNLog这样的日志库,甚至是。net的Diagnostic类。它们都可以在多线程中正常工作。

IProgress<T>仍然可以在这里提供帮助,因为处理Report事件的委托可以简单地将消息写入您已经在UI线程上创建的日志文件中。

你可以这样写:

var progress = new Progress<LogEntry>(entry =>
{
   _logFile.WriteLine("{0} : {1}",entry.EntryType,entry.Message);
});

和从Task内部报告:

var entry=new LogEntry(...);
progress.Report(entry);