异步等待继续未按预期工作

本文关键字:工作 等待 继续 异步 | 更新日期: 2023-09-27 18:29:43

我正在开发一个WPF Prism应用程序。我有一个DelgateCommand,它负责填充由UI线程异步拥有的ObservableCollection 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 });
                    }
                }
            });
    }

我面临的问题是,在设置等待的异步方法完成后,我设置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>

不确定问题出在哪里。有人知道是什么原因造成的吗

异步等待继续未按预期工作

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

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

有关EnableCollectionSynchronization的更多信息,请参阅此处。

您可以这样做;

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