异步任务永远不会在简单的API客户端中结束。僵局?

时间:2015-01-23 13:11:24

标签: c# .net async-await deadlock asynchttpclient

我是C#的新手,我很可能误解了await,async和Tasks的正确用法:)

我想开发一个类(OWConnector)作为我的应用程序的API客户端,为此我开发了一个通用的PostRequest方法来执行POST请求。

不幸的是,当我使用auth方法(使用泛型方法PostRequest)时,应用程序看起来会陷入死锁。

你能帮我理解问题出在哪里吗?我在代码中标记了调试器永远等待。

// the debugger get stacked here :(

表格方法

 private void btnAnalyze_Click(object sender, EventArgs e)
 {
    OWConnector api = new OWConnector(@"http://mywebsite.com/");
    Boolean didAuth = api.auth("myuser", "mypass");
    if (didAuth)
    {
        MessageBox.Show(@"success :)");
    }
    else
    {
         MessageBox.Show(@"failed :(");
    }
}

OWConnector类

class OWConnector
{
    private CookieContainer cookieJar;
    private HttpClientHandler handler;
    private HttpClient client;
    private Uri baseUri; 

    public OWConnector(string baseUrl)
    {
        baseUri = new Uri(baseUrl);
        cookieJar = new CookieContainer();
        handler = new HttpClientHandler();
        handler.CookieContainer = cookieJar;
        handler.UseCookies = true;
        handler.AllowAutoRedirect = false;

        client = new HttpClient(handler);
        client.BaseAddress = baseUri;
    }

    public async Task<RequestResponse> PostRequest(string url, HttpContent data = null)
    {
        RequestResponse response = new RequestResponse();

        try
        {
            // the debugger get stacked here :(
            response.httpResponse = await client.PostAsync(url, data);  
        }
        catch (System.AggregateException e)
        {
            response.error = true;
            foreach (Exception ie in e.InnerExceptions)
            {
                response.errorMessage += ie.GetType().ToString() + ": " + ie.Message + "\n";
            }
        }

        return response;
    }

    public Boolean auth(string username, string password)
    {
        var content = new FormUrlEncodedContent(new[]{
            new KeyValuePair<string, string>(@"form_name", @"sign-in"),
            new KeyValuePair<string, string>(@"identity", username),
            new KeyValuePair<string, string>(@"password", password),
            new KeyValuePair<string, string>(@"remember", @"on"),
            new KeyValuePair<string, string>(@"submit", @"Sign In"),
        });

        RequestResponse r = PostRequest(@"/", content).Result;
        Boolean cookieFound = false;
        foreach (Cookie c in cookieJar.GetCookies(baseUri))
        {
            if (c.Name == @"ow_login")
            {
                cookieFound = true;
            }
        }

        return cookieFound;
    }
}

class RequestResponse
{
    public Boolean error;
    public string errorMessage;
    public HttpResponseMessage httpResponse;

    public RequestResponse()
    {
        error = false;
        errorMessage = @"";
    }
}

1 个答案:

答案 0 :(得分:3)

您使用Task.Result阻止了异步操作。你正在做sync over async

您的流程应始终async从事件处理程序(应该是async void)一直向下(使用async Task)。

private async void btnAnalyze_Click(object sender, EventArgs e)
{
    OWConnector api = new OWConnector(@"http://mywebsite.com/");
    Boolean didAuth = await api.authAsync("myuser", "mypass");
    if (didAuth)
    {
        MessageBox.Show(@"success :)");
    }
    else
    {
         MessageBox.Show(@"failed :(");
    }
}

class OWConnector
{
    // same as in OP...

    public async Task<bool> authAsync(string username, string password)
    {
        var content = new FormUrlEncodedContent(new[]{
            new KeyValuePair<string, string>(@"form_name", @"sign-in"),
            new KeyValuePair<string, string>(@"identity", username),
            new KeyValuePair<string, string>(@"password", password),
            new KeyValuePair<string, string>(@"remember", @"on"),
            new KeyValuePair<string, string>(@"submit", @"Sign In"),
        });

        RequestResponse r = await PostRequest(@"/", content);
        Boolean cookieFound = false;
        foreach (Cookie c in cookieJar.GetCookies(baseUri))
        {
            if (c.Name == @"ow_login")
            {
                cookieFound = true;
            }
        }

        return cookieFound;
    }
}

死锁的原因可能是SynchronizationContext并且您正在阻止需要发布到SC完成的任务(因此,死锁)。虽然async一直解决了问题,但您也应该注意忽略捕获的SC的ConfigureAwait(false)。所以更好的authAsync将是:

public async Task<bool> authAsync(string username, string password)
{
    var content = new FormUrlEncodedContent(new[]{
        new KeyValuePair<string, string>(@"form_name", @"sign-in"),
        new KeyValuePair<string, string>(@"identity", username),
        new KeyValuePair<string, string>(@"password", password),
        new KeyValuePair<string, string>(@"remember", @"on"),
        new KeyValuePair<string, string>(@"submit", @"Sign In"),
    });

    RequestResponse r = await PostRequest(@"/", content).ConfigureAwait(false);
    Boolean cookieFound = false;
    foreach (Cookie c in cookieJar.GetCookies(baseUri))
    {
        if (c.Name == @"ow_login")
        {
            cookieFound = true;
        }
    }

    return cookieFound;
}