我正在开发 .NET MAUI 应用程序,并遇到了与异步编程相关的潜在死锁问题。我正在寻求有关如何正确使用
ConfigureAwait(false)
以避免死锁并提高性能的建议。
在我的应用程序中,我有几个与
ViewModel
交互的异步方法,用于执行数据获取、UI 更新和页面导航等任务。最近,我注意到某些 UI 交互会导致死锁,导致 UI 变得无响应,并且某些操作无限期挂起。
这是我的代码的简化版本:
<ScrollView Orientation="Both" FlowDirection="RightToLeft"
HorizontalOptions="Start"
x:Name="ItemsScroller"
Scrolled="ScrollView_Scrolled"
HorizontalScrollBarVisibility="Always">
<VerticalStackLayout>
<HorizontalStackLayout HeightRequest="48" BackgroundColor="#F5F6F7">
<!-- Header Labels -->
<Label Text="کد کالا" Style="{StaticResource LableStyle}" WidthRequest="150" FontAttributes="Bold"/>
<Label Text="کالا" Style="{StaticResource LableStyle}" WidthRequest="300" FontAttributes="Bold"/>
<Label Text="نرخ" Style="{StaticResource LableStyle}" WidthRequest="150" FontAttributes="Bold"/>
<Label Text="مصوبه" Style="{StaticResource LableStyle}" WidthRequest="200" FontAttributes="Bold"/>
<Label Text="تاریخ مصوبه" Style="{StaticResource LableStyle}" WidthRequest="100" FontAttributes="Bold"/>
<Label Text="شماره مصوبه" Style="{StaticResource LableStyle}" WidthRequest="100" FontAttributes="Bold"/>
<Label Text="موجودی" Style="{StaticResource LableStyle}" WidthRequest="100" FontAttributes="Bold"/>
</HorizontalStackLayout>
<CollectionView HorizontalScrollBarVisibility="Never" VerticalScrollBarVisibility="Never"
VerticalOptions="StartAndExpand"
SelectionMode="Multiple" SelectedItems="{Binding SelectedProduct, Mode=TwoWay}"
ItemsSource="{Binding Goods}"
x:Name="GoodsCollectionView">
<CollectionView.ItemTemplate>
<DataTemplate>
<HorizontalStackLayout HeightRequest="48">
<!-- Data Template for items -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="{Binding Color}"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#C8E6FF"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- Item Labels -->
<Label Text="{Binding ProductCode}" Style="{StaticResource LableStyle}" WidthRequest="150"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding ProductName}" Style="{StaticResource LableStyle}" WidthRequest="300"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding Rate, StringFormat='{0:N0}'}" Style="{StaticResource LableStyle}" WidthRequest="150"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding MosavabeSharh}" Style="{StaticResource LableStyle}" WidthRequest="200"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding MosavabeDate}" Style="{StaticResource LableStyle}" WidthRequest="100"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding MosavabeNo, StringFormat='{0:N0}'}" Style="{StaticResource LableStyle}" WidthRequest="100"/>
<Line Stroke="#DFDFE0" Y2="40" VerticalOptions="Center"/>
<Label Text="{Binding MojodiVaghee, StringFormat='{0:N0}'}" Style="{StaticResource LableStyle}" WidthRequest="100"/>
</HorizontalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
private async void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
{
if (!onNavigated) return;
var scrollView = sender as ScrollView;
if (scrollView != null && scrollView.ScrollY >= scrollView.ContentSize.Height - scrollView.Height)
{
await _viewModel.RunIsBusyTaskAsync(async () =>
{
start += 49;
end += 49;
await _viewModel.UpdateDisplayedItems(true, start, end);
}).ConfigureAwait(false);
}
}
public async Task UpdateDisplayedItems(bool IsClear, int start, int end)
{
var result = await GoodToList(new ProductsRequest
{
Start = start,
End = end
}).ConfigureAwait(false);
if (result == null || !result.Any())
return;
if (IsClear)
{
MainThread.BeginInvokeOnMainThread(() =>
{
result.ForEach(Goods.Add);
var productcode = new HashSet<string>(product.Select(c => c.ProductCode));
var userItemss = result.Where(x => productcode.Contains(x.ProductCode));
AddRange(SelectedProduct, userItemss);
});
return;
}
else
{
Goods = new ObservableCollection<Good>(result);
var userItems = result.Where(x => product.Select(c => c.ProductCode).Contains(x.ProductCode));
SelectedProduct = userItems.Any() ? new ObservableCollection<object>(userItems) : new ObservableCollection<object>();
}
}
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Mopups.Services;
using POSApp.Views.Popups;
using System.Collections.ObjectModel;
namespace POSApp.ViewModel
{
public partial class BaseViewModel : ObservableObject
{
private bool IsBusy, SecondIsBusy;
private Loading loadingPopup;
public IAsyncRelayCommand<object> OpenBrowserCommand { get; private set; }
public IAsyncRelayCommand<object> MakePhoneCallCommand { get; private set; }
public BaseViewModel()
{
loadingPopup = new Loading();
OpenBrowserCommand = new AsyncRelayCommand<object>(OpenBrowserAsync);
MakePhoneCallCommand = new AsyncRelayCommand<object>(MakePhoneCallAsync);
}
public async Task RunIsBusyTaskAsync(Func<Task> awaitableTask)
{
if (IsBusy) return;
IsBusy = true;
try
{
await MopupService.Instance.PushAsync(loadingPopup);
await awaitableTask();
}
catch (Exception) { }
finally
{
if (MopupService.Instance.PopupStack.Any())
await MopupService.Instance.PopAsync();
IsBusy = false;
}
}
public async Task AnimationClicked(object sender)
{
if (sender is VisualElement animatable)
{
await animatable.ScaleTo(0.75, 100);
await animatable.ScaleTo(1, 100);
}
}
public void AddRange<T>(ObservableCollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
{
collection.Add(item);
}
}
}
}
尽管在
ConfigureAwait(false)
方法中使用了 UpdateDisplayedItems
,我仍然遇到死锁和 UI 元素无响应的情况。尤其是在处理由 UI 交互触发的异步操作(例如滚动)时,尤其会发生死锁。
ConfigureAwait(false)
来避免 .NET MAUI 应用程序上下文中的死锁?任何见解或建议将不胜感激。
谢谢!
@movahhed 这个问题你解决了吗?