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

时间:2016-01-11 09:04:37

标签: c# wpf multithreading asynchronous async-await

我正在处理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 });
                    }
                }
            });
    }

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

await ShowRequestsVsDaysAsync();
  

因此,即使在异步方法之前也设置了IsChartBeingPopulated   完成了。

     

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

ShowPatientVisitsVsDaysCommand = new DelegateCommand(ShowPatientVisitsVsDays);

IsChartBeingPopulated用于设置属于'扩展WPF工具包的IsBusy DependencyProperty控件的BusyIndiator。 我们的想法是在绑定集合中填充图表数据时显示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>

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

2 个答案:

答案 0 :(得分:2)

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

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

有关EnableCollectionSynchronization的详细信息,请参阅here

答案 1 :(得分:0)

你可以做这样的事情;

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