Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Async/await用于具有进度/取消的长时间运行的API方法_C#_Backgroundworker_Async Await - Fatal编程技术网

C# Async/await用于具有进度/取消的长时间运行的API方法

C# Async/await用于具有进度/取消的长时间运行的API方法,c#,backgroundworker,async-await,C#,Backgroundworker,Async Await,编辑 我认为强制wait异步调用worker的正确方法是使用Task.Run,如下所示: await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress))); 从他那里得到了一些光 这应该很容易,但我对async/Wait还不熟悉,所以请耐心等待。我正在构建一个类库,用一些长时间运行的操作公开一个API。在过去,我使用BackgroundWorker处理进度报告和取消,如下面的

编辑 我认为强制wait异步调用worker的正确方法是使用Task.Run,如下所示:


await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress)));
从他那里得到了一些光


这应该很容易,但我对async/Wait还不熟悉,所以请耐心等待。我正在构建一个类库,用一些长时间运行的操作公开一个API。在过去,我使用BackgroundWorker处理进度报告和取消,如下面的简化代码片段:


public void DoSomething(object sender, DoWorkEventArgs e)
{
            BackgroundWorker bw = (BackgroundWorker)sender;
            // e.Argument is any object as passed by consumer via RunWorkerAsync...

            do
            {
                // ... do something ...

                // abort if requested
                if (bw.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                } //eif

                // notify progress
                bw.ReportProgress(nPercent);
            }
}
客户端代码如下所示:


BackgroundWorker worker = new BackgroundWorker
{ WorkerReportsProgress = true,
    WorkerSupportsCancellation = true
};
worker.DoWork += new DoWorkEventHandler(_myWorkerClass.DoSomething);
worker.ProgressChanged += WorkerProgressChanged;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(someparam);
现在我想利用新的异步模式。首先,我要在API中编写一个简单的长时间运行的方法;这里我只是逐行阅读一个文件,只是为了模拟一个真实的过程,在这个过程中,我必须通过一些处理转换文件格式:


public async Task DoSomething(string sInputFileName, CancellationToken? cancel, IProgress progress)
{
    using (StreamReader reader = new StreamReader(sInputFileName))
    {
        int nLine = 0;
        int nTotalLines = CountLines(sInputFileName);

        while ((sLine = reader.ReadLine()) != null)
        {
            nLine++;
            // do something here...
      if ((cancel.HasValue) && (cancel.Value.IsCancellationRequested)) break;
      if (progress != null) progress.Report(nLine * 100 / nTotalLines);
        }
        return nLine;
    }
}
对于这个示例,假设这是DummyWorker类的一个方法。现在,这里是我的客户端代码(一个WPF测试应用程序):


IProgress接口的实现来自,因此您可以引用该URL。无论如何,在这个使用测试中,UI被有效阻止,我看不到任何进展。那么,关于消费代码,这种情况的全貌是什么呢?

正如那篇博文顶部所指出的,那篇博文中的信息已经过时了。您应该使用.NET 4.5中提供的新的
IProgress
API

如果您使用的是阻塞I/O,请将您的核心方法阻塞:

public void Build(string sInputFileName, CancellationToken cancel, IProgress<int> progress)
{
  using (StreamReader reader = new StreamReader(sInputFileName))
  {
    int nLine = 0;
    int nTotalLines = CountLines(sInputFileName);

    while ((sLine = reader.ReadLine()) != null)
    {
      nLine++;
      // do something here...
      cancel.ThrowIfCancellationRequested();
      if (progress != null) progress.Report(nLine * 100 / nTotalLines);
    }

    return nLine;
  }
}

或者,您可以重写
Build
以使用异步API,然后直接从事件处理程序调用它,而无需将其包装在
Task中。Run

是的,
Task.Run()
就是这样做的方法。或者,如果操作确实很长,则可能设置了
Task.Factory.StartNew()
,并设置了
LongRunning
选项。谢谢,我通过删除异步并将返回类型更改为void来阻止我的方法。请注意,不允许您从Build()访问任何表单UI元素方法。@LefterisE:这就是progress reporter的作用。@StephenCleary您能详细介绍一下“异步API,然后直接从事件处理程序调用它”吗?我一直认为(推荐的)方法是将同步任务包装在task.Run()中。@public敌手:一点也不
Task.Run
应仅用于从不能使其异步的UI线程调用的同步代码。最好从HTTP请求和DB访问之类的“叶子”开始,让它们使用
*Async
方法,并允许异步代码向UI扩展。
public void Build(string sInputFileName, CancellationToken cancel, IProgress<int> progress)
{
  using (StreamReader reader = new StreamReader(sInputFileName))
  {
    int nLine = 0;
    int nTotalLines = CountLines(sInputFileName);

    while ((sLine = reader.ReadLine()) != null)
    {
      nLine++;
      // do something here...
      cancel.ThrowIfCancellationRequested();
      if (progress != null) progress.Report(nLine * 100 / nTotalLines);
    }

    return nLine;
  }
}
private async void OnDoSomethingClick(object sender, RoutedEventArgs e)
{
  OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" };
  if (dlg.ShowDialog() == false) return;

  // show the job progress UI...

  CancellationTokenSource cts = new CancellationTokenSource();
  DummyWorker worker = new DummyWorker();
  var progress = new Progress<int>((_, value) => { _progress.Value = value; });
  await Task.Run(() => builder.Build(dlg.FileName, cts.Token, progress);

  // hide the progress UI...
}