等待/异步任务不等待

时间:2017-04-13 21:38:09

标签: c# async-await task

我遇到了与Tasks和async / await关键词的混淆。我知道你不应该混合异步和阻塞代码。或者至少我对混合它们的解释是:

不要打电话来阻止来自非异步方法的API。所以这就是我的问题。

我正在尝试等待一个方法,然后相应地更新UI。问题是等待异步方法()调用的唯一方法是来自内部和异步方法()。

以下是一个例子:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
        {
            Completed = false;                    

            var cancellationTokenSource = new CancellationTokenSource();

            await RunValidation(options, cancellationTokenSource.Token);

            Completed = true;

        }));
    }
}

此代码正确运行该方法并等待。问题是我什么时候回来。出于某种原因,在设置“完成”标志时,不会切换依赖于此标志的按钮。如果我评论等待代码,则按钮正确切换。所以假设我没有返回UI线程,所以我尝试使用这个代码:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>(async (options) =>
        {
            Completed = false;                    

            var cancellationTokenSource = new CancellationTokenSource();                                                                

            var context = TaskScheduler.FromCurrentSynchronizationContext();

            await RunValidation(options, cancellationTokenSource.Token).ContinueWith(t => Completed = true, context);

            //Completed = true;

        }));
    }
}

这是RunValidation()方法:

private async Task RunValidation(Options options, CancellationToken token)
{            
    await _someService.SomAsyncMethod(options, token));
}

如果您注意到,ExecuteCommand在传递给命令的(options)参数之前有一个异步关键字。如果我删除了异步关键字,那么我必须修改对RunValidation()方法的调用。我仍然需要等待,所以这就是我所做的:

private RelayCommand<Options> _executeCommand;

public RelayCommand<Options> ExecuteCommand
{
    get
    {                
        return _executeCommand ?? (_executeCommand = new RelayCommand<Options>((options) =>
        {
            Completed = false;                    

            var context = TaskScheduler.FromCurrentSynchronizationContext();

            var cancellationTokenSource = new CancellationTokenSource();

            Task.Run(async () => await RunValidation(options, cancellationTokenSource.Token));

            Completed = true;

        }));
    }
}

此代码的问题在于它没有等待。所以我不知所措。

任何人都可以为我解释这个问题。我已经花了2天以上的时间,我还在这里。

谢谢, 添

以下是命令按钮的绑定。

private readonly Independent<bool> _completed = new Independent<bool>(true);

public bool Completed
{
    get { return _completed; }
    set { _completed.Value = value; }
}

private ICommand _doneCommand;

public ICommand DoneCommand
{
    get
    {
        return _doneCommand ?? (_doneCommand = MakeCommand.When(() => Completed).Do(() =>
        {
            DoSomething();
        }));
    }
}

private ICommand _cancelCommand;

public ICommand CancelCommand
{
    get
    {
        return _cancelCommand ??
               (_cancelCommand = MakeCommand.When(() => !Completed).Do(() => DoSomthingElse()));
    }
}

我正在使用Michael Perry的UpdateControls库中的MakeCommand对象。它们包含依赖性跟踪,当完成属性更改时,它会引发CanExecuteChange事件。

2 个答案:

答案 0 :(得分:0)

您的第一个代码是正确的。很可能您的Completed属性的实施不正确。您的视图模型对象应该实现INotifyPropertyChanged。最简单的方法是使用提供功能的基类。 ReactiveUI是我一直使用的nuget包。用法就像

一样简单
public class MyObject : ReactiveObject {

    private bool _Completed;
    public bool Completed {
        get => _Completed;
        set => this.RaiseAndSetIfChanged(ref _Completed, value);
    }

}

这将确保在更改属性时将通知引发到UI。

如果您希望它更具魔力,可以使用ReactiveUI.Fody,然后您的代码将缩减为

public class MyObject : ReactiveObject {

    [Reactive]
    public bool Completed { get; set;}

}

答案 1 :(得分:0)

所以问题实际上是第三方图书馆。我用它来为2个按钮提供依赖性跟踪。因此,当完整标志发生更改时,它会为两个按钮引发CanExecuteChange事件,而无需编写代码来执行此操作。不幸的是,它在引入async / await调用后停止了工作。我用RelayCommands替换了2个MakeCommands并自己引发了事件,一切正常。

感谢大家的回复。