【问题标题】:await / async task not waiting等待/异步任务不等待
【发布时间】:2017-04-15 03:23:19
【问题描述】:

我对 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;

        }));
    }
}

此代码正确运行该方法并等待。问题是我什么时候回来。由于某种原因,在设置 Complete 标志时,依赖于该标志的按钮不会被切换。如果我评论等待代码,那么按钮会正确切换。所以假设我没有在 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 在传递给命令的(选项)参数之前有一个异步关键字。如果我删除了 async 关键字,那么我必须修改对 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 对象。它们包含在 Complete 属性更改时引发 CanExecuteChange 事件的依赖性跟踪。

【问题讨论】:

  • 第一段代码是正确的。按钮如何绑定到 Completed 标志?可以观察到吗?
  • 由于您的问题是由于通知丢失造成的,并且由于您使用的库应该为您生成通知,因此我建议您使用 UpdateControls 库提出问题。
  • 所以我并没有发疯,从表面上看,我做事正确吗?
  • 你在 XAML 中的 ExecuteCommand 绑定中设置 IsAsync=true 了吗?
  • 我不熟悉那个库,所以可能还有更多事情要做,但是对于普通的 MVVM,您需要在设置 Complete 属性时触发 NotifyPropertyChanged 事件,以便通知绑定到它的任何内容.这只是一个建议,根据设置的内部工作原理,可能不需要。可能会添加它,看看它是否会改变?

标签: c# async-await task


【解决方案1】:

您的第一个代码是正确的。很可能您的 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;}

}

【讨论】:

    【解决方案2】:

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

    感谢大家的回复。

    提姆

    【讨论】:

      猜你喜欢
      • 2014-09-06
      • 1970-01-01
      • 2014-03-18
      • 2016-07-07
      • 2016-03-25
      • 2017-10-09
      • 2013-02-10
      相关资源
      最近更新 更多