为什么等待永远不会回来?

时间:2017-08-02 10:25:11

标签: c# async-await task-parallel-library

我看到等待似乎永远不会回来。这是示例代码:

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

OnStart被调用会发生什么,它将状态设置为"正在运行...",然后调用StartProcessing。五秒钟失效,但我从未看到状态设置为"完成!"

如果我打电话给OnStop,那么任务就会被取消,我会看到"完成!"状态。

我猜我是在创建一个任务以及async / await创建的任务,但它是挂起还是死锁?

这是WPF XAML代码:

<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>

2 个答案:

答案 0 :(得分:6)

您正在创建new Task但未启动它,因此它永远不会完成。而是使用Task.Runawait

await Task.Run(() => { });

还要考虑使用Task.Delay而不是Thread.Sleep,这样就不会阻止当前线程,

答案 1 :(得分:5)

您希望避免在方法上使用async void。更新StartProcessing以返回Task,您还应使用Task.Delay代替Thread.Sleep

private async Task StartProcessing() {
    try {
        await Task.Delay(5000, _cancellationTokenSource.Token);
    } catch (TaskCanceledException e) {
        Debug.WriteLine($"Expected: {e.Message}");
    }
    Status = "Done!";
}

接下来,如果OnStart实际上是一个事件处理程序,那么它是允许async void的一个例外。将OnStart更新为异步,然后等待现在等待的StartProcessing

private async void OnStart(object sender, RoutedEventArgs e) {
    Status = "Running...";

    _cancellationTokenSource = new CancellationTokenSource();

    await StartProcessing();
}

最后我建议阅读

Async/Await - Best Practices in Asynchronous Programming By Stephen Cleary

更好地了解如何使用async / await