异步等待继续未按预期工作
本文关键字:工作 等待 继续 异步 | 更新日期: 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.");
});
}
}