异步任务永远不会在简单的API客户端中结束。僵局? [英] Async Task never ends in simple API client. Deadlock?
问题描述
我是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屋!