65.9K
CodeProject 正在变化。 阅读更多。
Home

IO 绑定异步/Await 任务示例,带取消选项

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2018 年 1 月 23 日

CPOL

4分钟阅读

viewsIcon

16813

downloadIcon

345

关于如何使用 IO 绑定异步/Await 进程的简短示例

引言

本文展示了一个小示例,说明如何在基于 WPF 的简单应用程序中使用 Async/Await 模式,以及用户如何通过 CancellationToken取消 当前启动的 线程

Using the Code

我们将首先展示演示应用程序的工作方式,然后我们将以 逐步的方式 详细介绍技术实现细节。

用户界面

应用程序非常简单。用户填写文本文件所在的完整路径,单击 Get 按钮,文本文件的内容将加载到主屏幕中(如果找到),否则将显示一个错误消息框。为了证明文本文件的处理(读取)是 async 的,用户可以在处理输入文件时单击 "+" 按钮。这将在主屏幕中显示一个整数值,该值将增加 1。在处理文件的同时,主屏幕中会显示一个百分比处理消息。最后,为了展示 CancellationTokens 的使用,当用户“点击” Cancel 按钮时,正在运行的线程(任务)将被中断,并且将向用户显示取消消息!

在进入代码之前...

备注 1:Regions 的使用...

依我之见,将相似的代码分组到 regions 中总是一个好主意,我通常使用以下 regions

  • Private Storage:变量声明
  • C'tor:构造函数代码
  • Private Interface:所有 private 方法,细分为 UIEventhandlersApplicationLogic
  • Public Interface:所有从外部可访问的代码(+ 最终的 ProtectedInternal 部分,如果需要的话...)。

备注 2:在 UI 代码隐藏中编写代码

为了简单起见...此示例中的所有事件处理和应用程序逻辑都已在 main-form代码隐藏 中编写。虽然这对于演示目的来说是可以的,但在生产编码中应该省略。相反,所有应用程序和事件处理逻辑都应该从 UI 代码隐藏中提取出来,并放入所谓的 View-Model 类中,采用 MVVM(Model-View-ViewModel)模式。

解释代码隐藏

Private Storage

#region Private Storage

private int _counter;
CancellationTokenSource _cts;

#endregion Private Storage

当用户单击 + 按钮时,将使用 _counter 变量来更新计数器。这只是为了向读者表明,UI 界面 在后台线程运行时(任务在用户激活 Get 按钮后执行 read-async)仍然是 响应的

获取文件内容事件处理方法

private async void buttonGetFile_Click(object sender, RoutedEventArgs e)
{
    try
    {
        _cts = new CancellationTokenSource();
        textBlockResult.Text = string.Empty;
        labelPlus.Content = string.Empty;
        labelProgress.Content = string.Empty;

        buttonGetFile.IsEnabled = false;
        textBlockResult.Text = await GetFileContentAsync(textBoxFileName.Text, _cts.Token);

    }
    catch (OperationCanceledException exCancel)
    {
        MessageBox.Show(exCancel.Message);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        buttonGetFile.IsEnabled = true;
    }
}

此方法将调用一个方法,该方法以 async 方式从文件读取数据。在调用 GetFileContentAsync(...)(读取磁盘上文件的内容)时,将 _ctsCancellationTokenSource.Token 属性作为参数发送。如果请求取消,则 Token 属性将传播取消消息。添加一个 catch 块,如果用户选择取消文件读取操作,则显示一条消息。当此 IO 绑定的后台线程运行时,UI 仍然对用户输入做出响应(单击 + 按钮来证明这一点)。

在执行后台线程期间,UI 保持响应

private void buttonPlus_Click(object sender, RoutedEventArgs e)
{
    labelPlus.Content = _counter++.ToString();
}

在后台线程执行期间(当用户提供了本地磁盘上物理文件的正确 URL 并通过点击 UI 中的 Get 按钮启动了 IO 绑定检索过程时),UI 保持响应,用户可以单击 + 按钮,该按钮将增加计数器。

async 方法的实现

private async Task<string> GetFileContentAsync(string fileName, CancellationToken ct)
{
    // mimic long running process ...
    for(int i = 0; i <= 100; i++)
    {
        if (!ct.IsCancellationRequested)
        {
            // mimic some long running process ...
            await Task.Delay(50);
            // update ui, we use dispatch to update the ui thread
            this.Dispatcher.Invoke(() => SetProgress(i.ToString(), fileName));
        }
    }

    using (StreamReader reader = new StreamReader(fileName))
    {
        if (!ct.IsCancellationRequested)
        {
            string fileContent = await reader.ReadToEndAsync();
            return fileContent;
        }
        throw new OperationCanceledException
             ($"File-read-async of {fileName} has been canceled by user !");
    }
}

private void SetProgress(string progress, string fileName)
{
   labelProgress.Content = $"file {fileName} - {progress} % processed ...";
}

private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
   if (_cts != null)
      _cts.Cancel();
}

代码非常简单,该方法提供了一个 fileNameCancellationToken 作为输入参数,将返回一个 Task of string 作为返回值。一旦 Task 完成,控制权将返回给 调用方法 (buttonGetFile_Click),结果将显示在 UI 中。由于 async 执行上下文,UI 将保持响应,以便用户在后台进程运行时可以执行其他操作... 如果用户点击 Cancel 按钮,则正在进行的线程将被取消。备注:由于 async 读取过程不需要很长时间即可完成,因此我通过引入延迟来模拟一个长时间运行的进程。还要注意,我们会在每个时间间隔通知 UI 线程。我们需要执行 this.Dispatcher.Invoke(...) 方法,因为我们正在运行的后台进程在与 UI 线程不同的线程上运行。

关注点

请注意,上面提到的示例是一个 IO 绑定的 async 进程,这与 CPU 绑定的 async 进程相反,可能需要以不同的方式处理,更具体地说,我们应该(与 CPU 绑定相反)尝试在执行 IO 密集型任务时将线程返回给线程池。通过这种方式,线程可以被 windows-scheduler 重新使用以处理不同的操作。为了简单起见,我没有在这个简单的示例中包含这种行为,但请查看(除其他外)下一个 URL 关于这个概念:return-task-threads-back-to-the-thread-pool

© . All rights reserved.