简体   繁体   English

通过任务报告/记录

[英]Reporting/logging from with in a Task

I have an async call that throws an exception if the given record fails processing. 我有一个异步调用,如果给定记录处理失败,该调用将引发异常。 That exception is caught as aggregated exception. 该异常被捕获为聚合异常。 There is now, a requirement that I have to log a warning message from my async method. 现在,有一个要求,我必须从异步方法中记录警告消息。 I cannot log to TextBox/Grid as it is not allowed to access controls over different threads and I cannot throw an exception as I actually want to log and continue that task. 我无法登录到TextBox / Grid,因为它不允许访问不同线程上的控件,并且由于我实际上想登录并继续执行该任务而无法引发异常。 Here is the parent code that launches a task: 这是启动任务的父代码:

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();
        }   
      }
    }
  }
}

In the above code, GenerateDataListInternal is the method that throws exceptions and now needs to log. 在上面的代码中, GenerateDataListInternal是引发异常并且现在需要记录的方法。

How can I log from within the GenerateDataListInternal method? 如何从GenerateDataListInternal方法中登录? I have tried the delegate approach and that simply hangs the application. 我已经尝试了委托方法,并且只是挂了应用程序。 I have a method that logs to Console, Grid and text file (in that order). 我有一个方法登录到控制台,网格和文本文件(按此顺序)。 Any call to that method from asynced methods fail due to Cross thread operation. 由于跨线程操作,从异步方法对该方法的任何调用都会失败。

This is already provided through the System.IProgress interface and the Progress class, where T can be any class, allowing you to report much more than a simple progress percentage. 这已经通过System.IProgress接口和Progress类提供了,其中T可以是任何类,从而使您报告的不仅仅是简单的进度百分比。

IProgress<T>.Report allows you to report progress (or anything else) from inside an asynchronous operation without worrying who will actually handle the report. IProgress<T>.Report允许您从异步操作内部报告进度(或其他任何东西),而不必担心谁会真正处理该报告。

Progress<T> will call an action and/or raise an event on the thread where it was created (eg. the UI thread) whenever your task calls .Report 每当您的任务调用.Report时, Progress<T>都会在创建它的线程(例如UI线程)上调用操作和/或引发事件。

This example from the .NET Framework Blog displays how easy it is to use IProgress<T> : .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;
}

The async process starts with this code: 异步过程从以下代码开始:

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);
}

Note that progressIndicator is created in the UI thread, so the ReportProgress method will be called on the UI thread. 请注意, progressIndicator是在UI线程中创建的,因此ReportProgress方法将在UI线程上调用。

UPDATE 更新

From the comments it sounds like you are trying to create your own logging solution and have issues with accessing the log file from multiple threads. 从注释中听起来,您似乎正在尝试创建自己的日志记录解决方案,并且在从多个线程访问日志文件时遇到问题。

The best solution here would be to use a logging library like log4net , NLog or even .NET's Diagnostic classes. 最好的解决方案是使用日志库,例如log4netNLog或什至.NET的诊断类。 All of them work without issues with multiple threads. 它们都可以在多个线程上正常工作。

IProgress<T> can still help here, as the delegate that handles a Report event can simply write the message to a log file you already created on the UI thread. IProgress<T>在这里仍然可以提供帮助,因为处理Report事件的委托可以将消息简单地写入到您已经在UI线程上创建的日志文件中。

You could write something like this: 您可以这样写:

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

and report from inside a Task with: 并从任务内部报告:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM