异步任务永远不会在简单的API客户端中结束。僵局? [英] Async Task never ends in simple API client. Deadlock?

查看:97
本文介绍了异步任务永远不会在简单的API客户端中结束。僵局?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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.

不幸的是,当我使用auth方法(使用通用方法 PostRequest )。

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 :(

表单方法

 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类

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 = @"";
    }
}


推荐答案

您通过使用 Task.Result 阻止异步操作。您正在通过 async进行同步

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

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

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

死锁的原因可能是因为 SynchronizationContext ,您正在阻塞需要发布到SC才能完成的任务(因此,出现死锁)。虽然 async 一直可以解决问题,但您还应该知道 ConfigureAwait(false)会忽略捕获的SC。因此,更好的 authAsync 将是:

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

这篇关于异步任务永远不会在简单的API客户端中结束。僵局?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆