使用httpclient并返回值无法正常工作的任务

时间:2016-05-01 17:26:11

标签: c# .net xamarin.ios task

我环顾四周,看了几个关于如何设置的教程,我没有运气。我正在使用Xamarin构建跨平台应用程序。我有一个可移植的类库来处理我的所有业务逻辑和Web服务调用。

我遇到的问题是,当我在下面的方法中运行代码时,它的工作没有问题,但如果我按照下面所示的那样运行它需要很长时间。我相信它已陷入僵局,但正如我对此一样,我无法理解其中的原因。我希望我的Api电话在PCL中,所以我可以在每个平台之间分享它。

public async Task<LoginResponse> Login(string userName, string password)
{
    HttpClient client = new HttpClient();
    string baseUrl = "http://someURLhere/";

    client.BaseAddress = new Uri(baseUrl);

    string authData = string.Format("/IOSLogin?Username={0}&Password={1}&languageSettingOnDevice={2}", userName, password, "en");

    var uri = new Uri(string.Format("{0}{1}", baseUrl, authData));

    var response = await client.GetAsync(uri);
    if (response.IsSuccessStatusCode)
    {
       var content = await response.Content.ReadAsStringAsync();

       LoginResponse jsonResponse = JsonConvert.DeserializeObject<LoginResponse>(content);
       jsonResponse.loginSuccesfull = true;
       return jsonResponse;
    }
    else
    {
       return new LoginResponse() { loginSuccesfull = false };
    }
}

在ios版本的项目中,在按钮事件中,我使用此代码运行上面显示的任务。

Core.Service.AtmisService service = new Core.Service.AtmisService();
LoginResponse loginTask = service.Login(userName.Text, password.Text).Result;

我认为通过在调用任务结束时使用.Result时这样设置它会执行此任务并返回结果。我添加了断点,它进入登录方法并获取此var response = await client.GetAsync(uri);,然后它什么都不做。关于我做错了什么的任何想法。谢谢你的帮助。

2 个答案:

答案 0 :(得分:1)

参考这篇文章Async/Await - Best Practices in Asynchronous Programming

  

“始终异步”意味着你不应该混合同步和   异步代码没有仔细考虑后果。在   特别是,通过调用阻止异步代码通常是个坏主意   Task.Wait或Task.Result。这是一个特别常见的问题   将“脚趾间”浸入异步的程序员   编程,仅转换其应用程序的一小部分   将它包装在同步API中,以便应用程序的其余部分   与变化隔绝。不幸的是,他们遇到了问题   死锁。在MSDN上回答了许多与异步相关的问题之后   论坛,Stack Overflow和电子邮件,我可以说这是迄今为止的   一旦他们学习了基础知识,异步新人最常问的问题:   “为什么我的部分异步代码死锁?”

不要混用阻塞和异步代码。你应该一直去Async。你的死锁是因为.Result正在阻止。

你想做...

LoginResponse loginTask = await service.Login(userName.Text, password.Text);
  

总结第二条准则,你应该避免混合异步和   阻止代码。混合异步和阻塞代码可能导致死锁,   更复杂的错误处理和意外的上下文阻塞   线程。本指南的例外是Main方法   控制台应用程序,或 - 如果您是高级用户管理a   部分异步代码库。

答案 1 :(得分:0)

在使用按钮EventHandler的情况下,您可以执行以下操作:

private async void myButtonItem_Clicked(object sender, EventArgs e)
        {
            Core.Service.AtmisService service = new Core.Service.AtmisService();
            LoginResponse loginTask = await service.Login(userName.Text, password.Text).ConfigureAwait(false);
            //The rest of the code in this method may run in a different thread than the one that invoked the method. If you have ui code after this then avoid using ConfigureAwait(false)
        }

您可以找到有关避免死锁的更多信息here