Reputation: 1523
I'm seeing an await that never seems to return. Here's the sample code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _status;
private CancellationTokenSource _cancellationTokenSource;
public MainWindow()
{
InitializeComponent();
_status = "Ready";
DataContext = this;
}
public string Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged(nameof(Status));
}
}
private void OnStart(object sender, RoutedEventArgs e)
{
Status = "Running...";
_cancellationTokenSource = new CancellationTokenSource();
StartProcessing();
}
private void OnStop(object sender, RoutedEventArgs e)
{
_cancellationTokenSource.Cancel();
}
private async void StartProcessing()
{
try
{
await new Task(() =>
{
Thread.Sleep(5000);
}, _cancellationTokenSource.Token);
}
catch (TaskCanceledException e)
{
Debug.WriteLine($"Expected: {e.Message}");
}
Status = "Done!";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
What happens is OnStart gets called, it sets status to "Running...", then calls StartProcessing. Five seconds lapse, however I never see the status get set to "Done!"
If I call OnStop, then the task is cancelled and I see the "Done!" status.
I'm guessing I'm creating a task as well as the task created by async/await but it's hanging or deadlocking?
Here's the WPF XAML code:
<Window x:Class="CancellationSample.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="Cancellation Test" Height="350" Width="525">
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top">
<Button Width="70" Margin="5" Click="OnStart">Start</Button>
<Button Width="70" Margin="5" Click="OnStop">Stop</Button>
</StackPanel>
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem>
<TextBlock Text="{Binding Status}"/>
</StatusBarItem>
</StatusBar>
<Grid></Grid>
</DockPanel>
</Window>
Upvotes: 5
Views: 2257
Reputation: 11841
You are creating a new Task
but not starting it, so it never finishes. Instead use Task.Run and await
on that.
await Task.Run(() => { });
Consider also using Task.Delay rather than Thread.Sleep
so that you don't block the current thread,
Upvotes: 6
Reputation: 246998
You want to avoid using async void
on methods. Update StartProcessing
to return Task
and also you should use Task.Delay
instead of Thread.Sleep
private async Task StartProcessing() {
try {
await Task.Delay(5000, _cancellationTokenSource.Token);
} catch (TaskCanceledException e) {
Debug.WriteLine($"Expected: {e.Message}");
}
Status = "Done!";
}
Next if OnStart
is in fact an event handler it is the one exception where async void
is allowed. Update OnStart
to be async and then await the now awaitable StartProcessing
private async void OnStart(object sender, RoutedEventArgs e) {
Status = "Running...";
_cancellationTokenSource = new CancellationTokenSource();
await StartProcessing();
}
Finally I would suggest reading
Async/Await - Best Practices in Asynchronous Programming By Stephen Cleary
to get a better understanding of how to use async/await
Upvotes: 5