我有一个 WPF GUI,我想在其中按下一个按钮来开始一个很长的任务,而不会在任务期间冻结窗口。当任务正在运行时,我想获得进度报告,并且我想加入另一个按钮,可以在我选择的任何时间停止任务。
async和await:
1:你只能await在一个async方法中。
2:你只能await一个awaitable对象(即Task,ValueTask,Task<T>,IAsyncEnumerable<T>,等),这些对象环绕的的返回类型async的方法和await关键字解开他们。(参见包装和展开部分)
3:异步方法名称应始终以结尾Async以提高可读性并防止错误。
// Synchronous method:
TResult MethodName(params) { }
// Asynchronous method:
async Task<TResult> MethodNameAsync(params) { }
在async-await语法功能,让编译器放弃和收回在控制awaited Task中的async方法。
执行等待await任务完成并返回其结果,而不会阻塞主线程。
Task.RunTask在线程池中排队 a 。(除非它是一个纯操作。) 即该async方法不在另一个线程中运行。async并且await它们本身与线程创建没有任何关系。
所以通过放入async方法签名,你告诉编译器使用状态机来调用这个方法(目前没有线程)。然后通过运行一个Task你(重新)使用一个线程来调用任务内部的方法。通过执行await任务,您可以防止执行流越过该await行而不会阻塞 UI 线程。
private async void MyButton_ClickAsync(object sender, RoutedEventArgs e)
{
Task task = Task.Run(()=>
ExecuteLongProcedure(this, intParam1, intParam2, intParam3)
);
Task task = ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);
await task;
task.Wait();
}
介绍完异步后,怎么在异步任务中停止任务
使用 CancellationTokenSource 终止线程。
<Window x:Class="ProgressExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight" Height="93.258" Width="316.945">
<StackPanel>
<Button x:Name="Button_Start" Click="Button_Click">Start</Button>
<ProgressBar x:Name="ProgressBar_Progress" Height="20" Maximum="100"/>
<Button x:Name="Button_Cancel" IsEnabled="False" Click="Button_Cancel_Click">Cancel</Button>
</StackPanel>
</Window>
按下停止按钮this.currentCancellationSource.Cancel 会终止线程
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private CancellationTokenSource currentCancellationSource;
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Enable/disabled buttons so that only one counting task runs at a time.
this.Button_Start.IsEnabled = false;
this.Button_Cancel.IsEnabled = true;
try
{
// Set up the progress event handler - this instance automatically invokes to the UI for UI updates
// this.ProgressBar_Progress is the progress bar control
IProgress<int> progress = new Progress<int>(count => this.ProgressBar_Progress.Value = count);
currentCancellationSource = new CancellationTokenSource();
await CountToOneHundredAsync(progress, this.currentCancellationSource.Token);
// Operation was successful. Let the user know!
MessageBox.Show("Done counting!");
}
catch (OperationCanceledException)
{
// Operation was cancelled. Let the user know!
MessageBox.Show("Operation cancelled.");
}
finally
{
// Reset controls in a finally block so that they ALWAYS go
// back to the correct state once the counting ends,
// regardless of any exceptions
this.Button_Start.IsEnabled = true;
this.Button_Cancel.IsEnabled = false;
this.ProgressBar_Progress.Value = 0;
// Dispose of the cancellation source as it is no longer needed
this.currentCancellationSource.Dispose();
this.currentCancellationSource = null;
}
}
private async Task CountToOneHundredAsync(IProgress<int> progress, CancellationToken cancellationToken)
{
for (int i = 1; i <= 100; i++)
{
// This is where the 'work' is performed.
// Feel free to swap out Task.Delay for your own Task-returning code!
// You can even await many tasks here
// ConfigureAwait(false) tells the task that we dont need to come back to the UI after awaiting
// This is a good read on the subject - https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
// If cancelled, an exception will be thrown by the call the task.Delay
// and will bubble up to the calling method because we used await!
// Report progress with the current number
progress.Report(i);
}
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
// Cancel the cancellation token
this.currentCancellationSource.Cancel();
}
}