【问题标题】:Async Await continuation not working as expected异步等待继续未按预期工作
【发布时间】:2016-01-12 08:05:08
【问题描述】:

我正在开发一个WPF Prism 应用程序。我有一个DelgateCommand,它负责填充一个ObservableCollection,它由UI线程异步拥有using async and await。该集合又被绑定到一个图表。

为了使multiple threads能够访问集合,我启用了synchronization,如下:

BindingOperations.EnableCollectionSynchronization(ChartBoundCollection, _lock);

命令处理逻辑如下:

    private async void ShowPatientVisitsVsDays()
        {
          IsChartBeingPopulated = true;
          this.ChartSubTitle = "Requests Vs Days";
          this.SeriesTitle = "Days";
          ChartBoundCollection.Clear();
          await ShowRequestsVsDaysAsync();
          IsChartBeingPopulated = false;
        }

填充可观察集合的逻辑如下:

private async Task ShowRequestsVsDaysAsync()
    {
        await Task.Run(() =>
            {
                if (PatientVisits.Count() > 0)
                {
                    var days = PatientVisits.Select(p => p.VisitDate.Value.Date).Distinct();
                    foreach (var i in days)
                    {

                        var dayVisitCount = PatientVisits.Where(p => p.VisitDate.Value.Date == i).Count();
                        ChartBoundCollection.Add(new PatientVisitVsXAxis() { XAxis = i.ToShortDateString(), NumberOfPatientVisits = dayVisitCount });
                    }
                }
            });
    }

我面临的问题是,在设置了 await 的异步方法完成后,我设置 IsChartBeingPopulated = false 的延续没有得到执行。

await ShowRequestsVsDaysAsync();

因此 IsChartBeingPopulated 甚至在异步方法之前设置 完成了。

命令处理程序 ShowPatientVisitsVsDays() 由点击调用 视图上的按钮。该按钮绑定到以下 命令:

ShowPatientVisitsVsDaysCommand = new DelegateCommand(ShowPatientVisitsVsDays);

IsChartBeingPopulated 用于设置属于“扩展 WPF 工具包”的BusyIndiator 控件的IsBusy DependencyProperty。 这个想法是在绑定集合中填充图表数据时显示BusyIndicator

<xctk:BusyIndicator IsBusy="{Binding Path=IsChartBeingPopulated}" >
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <chart:ClusteredColumnChart Grid.Row="0" ChartTitle="Patient Visits History" ChartSubTitle="{Binding Path=ChartSubTitle}">
                <chart:ClusteredColumnChart.Series>
                    <chart:ChartSeries SeriesTitle="{Binding Path=SeriesTitle}" DisplayMember="XAxis" ValueMember="NumberOfPatientVisits" ItemsSource="{Binding Path=ChartBoundCollection}" />
                </chart:ClusteredColumnChart.Series>
            </chart:ClusteredColumnChart>
        </Grid>
    </xctk:BusyIndicator>

不确定是什么问题。有人知道是什么原因造成的吗?

【问题讨论】:

  • 您不应该将ShowPatientVisitsVsDays 类型更改为Task,并等待ShowPatientVisitsVsDays 调用,以消除异步状态吗?
  • 感谢您的评论托马斯。我无法更改 ShowPatientVisitsVsDays 的返回类型,因为它是一个命令处理程序。类似的情况是异步处理按钮的单击事件,在这种情况下,您也无法更改事件处理程序的签名以返回任务。
  • @lovecoding:你想知道,ShowRequestsVsDaysAsync 电话没有等待吗?此外,由于ShowRequestsVsDaysAsync 不包含异步代码,因此您应该使其同步并在ShowPatientVisitsVsDays 中包装到Task.Run

标签: c# wpf multithreading asynchronous async-await


【解决方案1】:

您没有同步对集合的访问。 BindingOperations.EnableCollectionSynchronization 不会神奇地使收集线程安全。它只确保数据绑定引擎不会在不获取锁的情况下枚举集合。

在添加和清除集合时,您仍然需要锁定_lock 对象。

有关EnableCollectionSynchronization 的更多信息,请参阅here

【讨论】:

    【解决方案2】:

    你可以这样做;

        private async void ShowPatientVisitsVsDays()
        {
            IsChartBeingPopulated = true;
            this.ChartSubTitle = "Requests Vs Days";
            this.SeriesTitle = "Days";
            new ChartBoundCollection().Clear();
            IsChartBeingPopulated = await ShowRequestsVsDaysAsync();//here we are waiting till the async method is finished.
        }
    
    
        private async Task<bool> ShowRequestsVsDaysAsync()
        {
           return await Task.Run(() =>
            {
                if (PatientVisits.Any())//replace Count with Any to avoid unwanted enumerations.
                {
                    var days = PatientVisits.Select(p => p.VisitDate.Value.Date).Distinct();
                    foreach (var i in days)
                    {
    
                        var dayVisitCount = PatientVisits.Count(p => p.VisitDate.Value.Date == i);
                        chartBoundCollection.Add(new PatientVisitVsXAxis() { XAxis = i.ToShortDateString(), NumberOfPatientVisits = dayVisitCount });
                    }
                }
                Thread.Sleep(5000);//this is for testing. Sleep the thread for 5secs. Now your busyIndicator must be visible for 5secs minimum.
                return false;//return false, so that we can use it to populate IsChartBeingPopulated 
            });
        }
    

    更新:我认为您对异步等待有一些疑问。下面的代码将帮助您清除它们。

    创建一个新的控制台应用程序并将此代码放在Program 类中。

    根据您在下面的评论,ShowPatientVisitsVsDays 中的文本应该在从“ShowRequestsVsDaysAsync”方法打印任何内容之前打印出来。它是这样工作的吗?对此进行测试并告诉我们。

       static void Main(string[] args)
        {
            Console.WriteLine("Main started");
            ShowPatientVisitsVsDays();
            Console.ReadLine();
        }
    
       private static async void ShowPatientVisitsVsDays()
        {
            await ShowRequestsVsDaysAsync();            
            Console.WriteLine("ShowPatientVisitsVsDays() method is going to SLEEP");
            Thread.Sleep(2000);
            Console.WriteLine("ShowPatientVisitsVsDays() method finished");
        }
    
       private static async Task ShowRequestsVsDaysAsync()
        {
            await Task.Run(() =>
            {
                Console.WriteLine("ASYNC ShowRequestsVsDaysAsync() is going to SLEEP.");
                Thread.Sleep(5000);
                Console.WriteLine("ASYNC ShowRequestsVsDaysAsync finished.");
            });
        }
     }
    

    【讨论】:

    • @Kosala:我已经尝试过你的建议,但奇怪的是等待异步方法完成后才会发生。
    • 你能告诉我们你怎么称呼ShowPatientVisitsVsDaysIsChartBeingPopulated的用法吗?
    • 我用忙碌指示器测试了一个类似的代码,它正在工作。我已将Thread.Sleep(5000); 添加到代码中。现在您的忙碌状态必须至少显示 5 秒。
    • 刚刚注意到您已将 ObservableCollection 用于ChartBoundCollection。这是什么原因?每次您在异步方法中向集合中添加新项目时,它都会通知 UI。这是您想要的还是应该在添加所有值后才刷新?如果是这样,请将其更改为列表。
    • 同意,添加 Thread.Sleep,确实会显示忙碌指示符。我还需要绑定到图表的 ObservableCollection,因为集合的内容将在运行时根据图表上上下文菜单项的单击进行修改。困扰我的问题是等待没有按预期工作。理想情况下,它应该让异步方法完成,然后执行延续,即 await 之后将 IsChartBeingPopulated 设置为 false 的代码行。
    猜你喜欢
    • 2019-04-19
    • 2020-03-04
    • 2019-04-07
    • 2022-01-22
    • 2018-12-02
    • 2021-07-15
    • 2021-12-08
    • 2023-03-03
    • 2019-08-31
    相关资源
    最近更新 更多