Async Task never ends in simple API client. Deadlock?

可紊 提交于 2019-12-11 18:04:08

问题


I'm new to C# and I'm very likely misunderstanding the the proper usage of await,async and Tasks :)

I want to develop a class (OWConnector) that serves as API client for my app, for this purpose I develop a generic PostRequest method to perform POST request.

Unfortunately the app looks like is going into a deadlock when I user the auth method (that uses the generic method PostRequest).

Can you help me to understand where is the problem ? I marked in the code where the debugger await forever.

// the debugger get stacked here :(

Form method

 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

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:


You're blocking on an asynchronous operation by using Task.Result. You're doing sync over async.

Your flow should be async all the way through from the event handler (that should be async void) all the way down (with 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;
    }
}

The reason for the deadlock is probably the fact that there's a SynchronizationContext and you are blocking on a task that need to post to the SC to complete (hence, deadlock). While async all the way solves the issue you should also be aware of ConfigureAwait(false) that disregards the captured SC. So an even better authAsync would be:

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;
}


来源:https://stackoverflow.com/questions/28110681/async-task-never-ends-in-simple-api-client-deadlock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!