Xamarin.Forms 中的死锁

Posted

技术标签:

【中文标题】Xamarin.Forms 中的死锁【英文标题】:Deadlock in Xamarin.Forms 【发布时间】:2020-06-06 21:56:34 【问题描述】:

我的 Xamarin 应用程序出现问题。该应用程序对我的网站使用自定义 API。我对async/await 方法没有太多经验。

以下代码显示了 App.xaml.cs:

public partial class App : Application

    public static bool IsUserLoggedIn  get; set; 
    public static UserModel currentLoggedInUser  get; set; 

    public static List<ActionTypeModel> listTypes;
    public static List<ActionSubTypeModel> listSubtypes;
    public static List<UserModel> listFriends;
    public static List<List<ActionModel>> listActiveActions;
    public static List<ActionModel> listLastAction;

    public App()
    
        this.InitializeComponent();

        APIHelper.InitializeClient();

        StartApp().Wait();
    

    private async Task StartApp()
    
        //// DEBUG
        //MyAccountStorage.Logout();

        string username = await MyAccountStorage.GetUsername().ConfigureAwait(false);
        string password = await MyAccountStorage.GetPassword().ConfigureAwait(false);
        string user_id = await MyAccountStorage.GetId().ConfigureAwait(false);
        if (username != null && password != null)
        

            currentLoggedInUser = new UserModel();
            if (user_id != null)
                currentLoggedInUser.user_id = Convert.ToInt32(user_id);
            currentLoggedInUser.username = username;
            currentLoggedInUser.password = password;

            bool isValid = false;
            isValid = await AreCredentialsCorrect(0, currentLoggedInUser.username, currentLoggedInUser.password).ConfigureAwait(false);
            if (isValid)
            
                IsUserLoggedIn = true;

                await FillLists().ConfigureAwait(false);

                MainPage = new NavigationPage(await MyPage.BuildMyPage().ConfigureAwait(false));
            
            else
            
                IsUserLoggedIn = false;

                MainPage = new NavigationPage(await LoginPage.BuildLoginPage().ConfigureAwait(false));
            

        
        else
        
            IsUserLoggedIn = false;

            MainPage = new NavigationPage(await LoginPage.BuildLoginPage().ConfigureAwait(false));
        
    

    private async Task FillLists()
    
        listFriends = await DataControl.GetFriends(App.currentLoggedInUser.user_id, App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
        if (listFriends == null)
            listFriends = new List<UserModel>();

        listTypes = await DataControl.GetTypes(App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
        if (listTypes == null)
            listTypes = new List<ActionTypeModel>();

        listActiveActions = new List<List<ActionModel>>();

        for (int i = 0; i < listTypes.Count; i++)
            listActiveActions.Add(await DataControl.GetActiveActions(listTypes[i].action_type_id, currentLoggedInUser.user_id, currentLoggedInUser.username, currentLoggedInUser.password).ConfigureAwait(false));

        listSubtypes = await DataControl.GetSubtypes(App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
        if (listSubtypes == null)
            listSubtypes = new List<ActionSubTypeModel>();

        listLastAction = await DataControl.GetLastAction(App.currentLoggedInUser.user_id, App.currentLoggedInUser.username, App.currentLoggedInUser.password).ConfigureAwait(false);
        if (listLastAction == null)
            listLastAction = new List<ActionModel>();
    

    public static async Task<bool> AreCredentialsCorrect(int type, string user, string pass, string nick = "", string email = "")
    
        List<UserModel> listUsers;

        if (type == 1)
            listUsers = await DataControl.CheckCredentials(1, user, pass, nick, email).ConfigureAwait(false);
        else
            listUsers = await DataControl.CheckCredentials(0, user, pass).ConfigureAwait(false);

        if (listUsers != null)
            if (listUsers.Any())
            
                currentLoggedInUser = listUsers.First();
                currentLoggedInUser.password = pass;
                return true;
            

        return false;
    

我在 DataControl.cs 中有 API:

public static async Task<List<UserModel>> CheckCredentials(int type, string username, string pass, string email = "", string nickname = "")

    string password = APIHelper.GetHashSha256(pass);

    string url = string.Empty;

    if (type == 0)
        url = APIHelper.ApiClient.BaseAddress + "/account/login.php?username=" + username + "&password=" + password;
    if (type == 1)
    
        string nick = string.Empty;
        if (string.IsNullOrEmpty(nickname) == false)
            nick = "&nickname=" + nickname;

        url = APIHelper.ApiClient.BaseAddress + "/account/signup.php?username=" + username + "&password=" + password + "&email=" + email + nick;
    
    if (string.IsNullOrEmpty(url))
        return null;

    using (HttpResponseMessage response = await APIHelper.ApiClient.GetAsync(url).ConfigureAwait(false))
    
        if (response.IsSuccessStatusCode)
        
            List<UserModel> listUsers = JsonConvert.DeserializeObject<List<UserModel>>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));

            return listUsers;
        
        else
            return null;
    

这是不同的async 方法之一。当我遗漏ConfigureAwait(false) 时,我遇到了死锁。当我将它添加到代码中时,我遇到了错误。

你能帮帮我吗?

【问题讨论】:

When I leave out the "ConfigureAwait(false)" I run into a deadlock - 这强烈表明你在某个地方是blocking on async。例如在您的constructor 中的StartApp().Wait();。不要这样做。 App 构造函数是我使用 Wait() 方法的唯一位置。其他所有方法都是异步的,并使用 await 来调用。 是的,一个就够了。 如果我删除 Wait() 应用程序什么也不做,我在调试输出中看到一个无穷无尽的线程池启动行和线程池完成... 您不能使用构造函数中的异步函数。您不仅需要删除Wait(这会导致fire-and-forget),还需要重新构建程序,以免从构造函数中调用异步方法。 【参考方案1】:

您可以使用Task.Run( //call your async method here );

所以在你的情况下:

 Task.Run(Startup);

【讨论】:

以上是关于Xamarin.Forms 中的死锁的主要内容,如果未能解决你的问题,请参考以下文章

ScrollView 问题中的 Xamarin.Forms 捏手势

Xamarin.Forms.Forms.Init(e) Onlaunched 中的 FileNotFoundExeception

ReactiveUI 中的 Xamarin.Forms 控件是不是需要自定义绑定?

Xamarin.Forms 中的 EmguCV 实现

Xamarin.Forms 无法更改本地设备上 Xamarin.iOS 中的应用程序图标

xamarin.forms 中的推送通知