异步任务永远不会在简单的 API 客户端中结束。僵局?
Async Task never ends in simple API client. Deadlock?
我是 C# 的新手,我很可能误解了 await、async 和 Tasks 的正确用法:)
我想开发一个 class (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
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
阻止异步操作。你在做 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
并且您正在阻止需要 post 到 SC 才能完成的任务(因此,死锁)。虽然 async
一路解决了问题,但您还应该注意 ConfigureAwait(false)
忽略了捕获的 SC。所以更好的 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;
}
我是 C# 的新手,我很可能误解了 await、async 和 Tasks 的正确用法:)
我想开发一个 class (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
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
阻止异步操作。你在做 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
并且您正在阻止需要 post 到 SC 才能完成的任务(因此,死锁)。虽然 async
一路解决了问题,但您还应该注意 ConfigureAwait(false)
忽略了捕获的 SC。所以更好的 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;
}