【问题标题】:Update UI in between 2 await sentences在 2 个 await 语句之间更新 UI
【发布时间】:2013-10-15 23:08:41
【问题描述】:

我正在尝试在(相当简单的)应用程序中实现异步等待的东西。 我的目标是在等待之间更新busyIndi​​cator。

我不知道是什么,但我认为我在理解 async await 的东西时遗漏了一些重要的东西。

private async void StartTest(object obj)
{
    try
    {
        this.IsBusy = true;
        this.BusyMessage = "Init..."

        await Task.Delay(7000);


        var getData1Task = this.blHandler.GetData1Async();
        this.BusyMessage = "Retreiving data...";

        this.result1 = await getDeviceInfoTask;
        this.result2 = await this.blHandler.GetData2Async();

        this.BusyMessage = "Searching...";

        this.result3 = await this.blHandler.GetData3();
    }
    finally
    {
        this.IsBusy = false;
        this.BusyMessage = string.empty;
    }
}

busyIndi​​cator 与IsBusyBusyMessage 绑定。 执行此代码时,我确实得到了显示“Init...”的busyIndi​​cator,但它永远不会更改为“正在检索数据...”或“正在搜索。 ..”。 更糟糕的是:在执行最终的GetData3 时,用户界面完全冻结。

【问题讨论】:

  • 这些方法有什么作用?它们真的是异步的吗?
  • 在调试器冻结时暂停它并查看堆栈跟踪。
  • 这段代码是否在事件处理程序中被调用?该处理程序是否标记为异步?
  • 我认为 finally-block 将在第一个 await 中断 方法时被调用。这可能会产生一些副作用。你能在 finally 块上放一个断点吗?
  • @Baldrick 此代码在我的视图模型中绑定到按钮的方法中。

标签: c# wpf async-await


【解决方案1】:

很可能GetData1AsyncGetData2AsyncGetData3 是同步方法(也就是说,我猜虽然它们确实返回了Task,但它们会同步完成所有工作)。在这种情况下,awaits 不会暂停该方法(因为返回的Task 将是一个已完成的任务)。因此,该方法将作为一个大的同步方法一直持续下去,并且 UI 将永远没有机会更新(因为在此期间它没有发送任何消息)。

如果您想要的不仅仅是猜测,请向我们展示这三种方法的代码。

【讨论】:

  • 它们确实是返回任务的同步方法。我想我已经找到了我错过的重要部分。我认为这些方法将在后台线程上运行。所以我想我必须在这里使用后台工作人员而不是异步等待?
  • @Koen,而不是 BackgroundWorker,请参阅 @SLaks 关于使用 Task.Run() 的回答。
【解决方案2】:

听起来您实际上想在后台线程上执行一个同步方法,然后在您的 UI 代码中异步等待它完成。

这正是Task.Run() 所做的。
它需要一个委托在 ThreadPool 中运行,然后返回一个 awaitable 任务给你结果。

【讨论】:

  • 谢谢。我会读一读,明天试一试。 @MattSmith 的回答已经给了我关于异步等待东西的重要部分。
【解决方案3】:

请您尝试通过 Dispatcher 推送消息吗?

    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                        { this.BusyMessage = "Retreiving data..."; }));

    // Do Something 


   App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
                    { this.BusyMessage = "Filtering  data..."; }));

   // Do Something

【讨论】:

    【解决方案4】:

    这是一个工作副本,其中包含我的一些假设。希望能帮助到你。我在一个正在运行的 WPF 应用程序中对其进行了测试。请注意,您发布的内容没有任何保护措施,以确保这不是“双重运行”(无论 IsBusy 的值如何,我们都可以开始,StartTest 可能应该确保这一点)。

    public class StackWork : ViewModelBase
    {
        private class MyHandler
        {
            private async Task<string> GetDataAsync(string result)
            {
                return await Task<string>.Run(() =>
                    {
                        Thread.Sleep(5000);
                        return result;
                    });
            }
    
            public async Task<string> GetData1Async()
            {
                return await GetDataAsync("Data1");
            }
    
            public async Task<string> GetData2Async()
            {
                return await GetDataAsync("Data2");
            }
    
            public async Task<string> GetData3()
            {
                return await GetDataAsync("Data3");
            }
        }
    
        private bool IsBusy { get; set; }
    
        private string _message = "";
        public string BusyMessage
        {
            get { return _message; }
            set { _message = value; RaisePropertyChanged("BusyMessage"); }
        }
        private MyHandler blHandler = new MyHandler();
        private Task<string> getDeviceInfoTask;
        private string result1 { get; set; }
        private string result2 { get; set; }
        private string result3 { get; set; }
    
        public StackWork()
        {
            getDeviceInfoTask = Task<string>.Run(() =>
                {
                    return ("device info");
                });
        }
    
        public async void StartTest(object obj)
        {
            try
            {
                this.IsBusy = true;
                this.BusyMessage = "Init...";
    
                await Task.Delay(7000);
    
                var getData1Task = /*this*/await/*was missing*/ this.blHandler.GetData1Async();
                this.BusyMessage = "Retreiving data...";
    
                //assuming this was Task.Run, put that in constructor
                this.result1 = getDeviceInfoTask/*this*/.Result/*was missing*/;
                this.result2 = await this.blHandler.GetData2Async();
    
                this.BusyMessage = "Searching...";
    
                //This was a little confusing because the name doesn't imply it is 
                //async, but it is awaited
                this.result3 = await this.blHandler.GetData3();
            }
            finally
            {
                this.IsBusy = false;
                this.BusyMessage = string.Empty;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-08-16
      • 1970-01-01
      • 2012-03-31
      • 2014-01-05
      • 1970-01-01
      • 2020-09-19
      • 1970-01-01
      • 2013-04-10
      • 1970-01-01
      相关资源
      最近更新 更多