[英]C# HTTP Request works in Console application but not in WinForms
最初我創建了一個控制台應用程序來進行一些測試,從服務器獲取api調用並使其完全正常運行。 現在,我想制作一個顯示器,但控制台應用程序中的相同確切代碼將無法在winforms應用程序中運行。 由於一些斷點測試,我已經看到它掛在這一行:
var response = await httpClient.SendAsync(request);
我不明白有什么區別,我已經下載了所有必要的軟件包。 我認為它與使用await有關。 謝謝,Ethan
這是請求方法代碼,其中包含私人信息。 我意識到最后有一些不必要的組件,但我只是從我最初的測試控制台應用程序中復制和粘貼。 使用此方法的所有方法(包括main)都是異步的。
public async Task<string> APICall(string address)
{
string error = "An error was encountered.";
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), address))
{
request.Headers.TryAddWithoutValidation("Api-Key", "API KEY");
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/hal+json"));
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
try
{
Stream res = await response.Content.ReadAsStreamAsync();
var serializer = new JsonSerializer();
using (var sr = new StreamReader(res))
{
using (var jsonTextReader = new JsonTextReader(sr))
{
return await response.Content.ReadAsStringAsync();
}
}
}
catch (IOException e)
{
Console.WriteLine(e);
}
catch (NullReferenceException e)
{
Console.WriteLine(e);
}
}
request.Dispose();
}
httpClient.Dispose();
}
return error;
}
這些是調用問題方法的調用方法,它從Main到startAsync到CallAsync,再到APICall,即所討論的方法。
static async Task Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 f = new Form1();
Caller caller = new Caller();
string test = await caller.CallAsync();
foreach (KeyValuePair<string, Server> server in Server.servers)
{
f.AddRow(server.Value.name, server.Value.location, server.Value.status, server.Value.ticketStatus);
}
Application.Run(f);
}
public static async Task startAsync()
{
Caller caller = new Caller();
string json = await caller.CallAsync();
Console.WriteLine(json);
dynamic name = JsonConvert.DeserializeObject<RootObject>(json);
List<Location> locs = name._embedded.locations;
int count = 0;
foreach (Location loc in locs)
{
count++;
Console.WriteLine(count);
Server.servers.Add(loc.name, new Server(loc.name, loc.address.city, loc.address.state, loc.id, loc.status, await caller.TicketStatusAsync(loc.id)));
}
}
public async Task<string> CallAsync()
{
return await APICall(baseAddress);
}
最有可能的是,調用堆棧,調用Wait()
或Result
或GetAwaiter().GetResult()
。 換句話說,您的代碼是同步異步。 這在Console應用程序中工作正常 - 事實上,這樣的一些阻塞是必要的,因此主控制台線程在異步工作完成之前不會退出 - 但它將在其他上下文中死鎖,包括WinForms。
它死鎖的原因是因為await
默認捕獲一個上下文,並使用該上下文繼續執行async
方法。 因此,當您await httpClient...
代碼運行時,它會捕獲當前上下文,然后返回一個不完整的任務。 然后,您的調用代碼將進一步阻塞,等待該任務完成。
對於WinForms UI線程,該上下文是WinFormsSynchronizationContext
,它始終在UI線程上執行代碼。 因此,當SendAsync
完成時,它將繼續在UI線程上執行async
方法。 但是,該UI線程被阻止,等待任務完成。 在UI線程空閑之前,任務無法完成,因此您遇到了死鎖。
修復此死鎖的正確方法是刪除sync-over-async反模式 。 換言之,代替Wait()
或Result
或GetAwaiter().GetResult()
與await
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.