任务等待激活。

11

我尝试使用Microsoft.Http来调用一个API。我按照这里提供的示例http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client,但我的任务没有完成,仍处于等待激活状态。

登录按钮:

    private void btnLogin_Click(object sender, EventArgs e) {
        try {
            var t = Api.LoginAsync( tbUsername.Text, tbPassword.Text);
            t.Wait();
            _Token = t.Result;
        }
        catch (Exception ex) {
            ShowError(ex.Message);
        }
    }

API类

static class Api {
    private const string URLBase = "http://localhost:28929/";

    public static async Task<string> LoginAsync(string Username, string Password ) {
        using (var client = new HttpClient()) {
            client.BaseAddress = new Uri(URLBase);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // HTTP POST
            var VM = new Usuario() { Username = Username, Password = Password};
            HttpResponseMessage response = await client.PostAsJsonAsync("api/login", VM);
            if (response.IsSuccessStatusCode) {
                return await response.Content.ReadAsAsync<string>();
            }
            else {
                throw new ApplicationException("No se pudo ingresar. Error: " + response.StatusCode.ToString());
            }
        }
    }
}

enter image description here

3个回答

22

你需要将事件处理程序也 async 化:

private async void btnLogin_Click(object sender, EventArgs e) {
    try {
        _Token = await Api.LoginAsync( tbUsername.Text, tbPassword.Text);

    }
    catch (Exception ex) {
        ShowError(ex.Message);
    }
}

2
调用Wait或检查任务的Result属性可能会导致死锁。 - Daniel Mann
1
哦,谢谢,浪费了太多时间。最终我使用了 _Token = await Api.LoginAsync(tbUsername.Text, tbPassword.Text); - Eduardo Molteni
很奇怪,甚至连编译都无法通过的答案如何被接受为正确答案? - Hamlet Hakobyan
@HamletHakobyan是第一个指引我正确方向的人。他现在已经编辑过了。 - Eduardo Molteni

9
你那里出现了死锁。 LoginAsync 方法会同步运行,直到第一个 await (await client.PostAsJsonAsync("api/login", VM);)。然后它将返回到事件处理程序,并且 UI 线程将在 t.Wait() 上被阻塞。当 await client.PostAsJsonAsync("api/login", VM); 完成时,方法的其余部分可以运行。问题是它必须在 UI 线程上运行(在相同的同步上下文中),而该线程已被阻塞,等待任务完成。死锁。
你也必须在事件处理程序中使用 await
private async void btnLogin_Click(object sender, EventArgs e) {
    try {
        _Token  = await Api.LoginAsync( tbUsername.Text, tbPassword.Text);
    }
    catch (Exception ex) {
        ShowError(ex.Message);
    }
}

你可以在Stephen Cleary的博客上找到详细的解释。

3

您可以尝试使用以下方法来绕过此问题:

private void btnLogin_Click(object sender, EventArgs e) {
    try {
        var task= Task.Run(async () =>
                   {
                       return await Api.LoginAsync( tbUsername.Text, tbPassword.Text);
                   });
         _Token  = task.Result; 
    }
    catch (Exception ex) {
        ShowError(ex.Message);
    }
}

我不建议这样做,但在某些情况下可以帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接