C# HTTP 请求在控制台应用程序中工作,但在 WinForms 中不起作用



最初,我创建了一个控制台应用程序来通过从服务器获取 api 调用进行一些测试,并使其功能齐全且正常工作。现在,我想制作一个显示,但控制台应用程序中的相同代码在 winforms 应用程序中不起作用。由于一些断点测试,我已经看到它被挂在了这一行上:

var response = await httpClient.SendAsync(request);

我不明白有什么区别,我已经下载了所有必要的软件包。我认为这与使用等待有关。谢谢,伊森

这是排除了私人信息的请求方法代码。我意识到最后有一些不必要的组件,但我只是从最初的测试控制台应用程序中复制和粘贴。使用此方法的所有方法(包括 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()ResultGetAwaiter().GetResult()。换句话说,您的代码是异步同步的。这在控制台应用程序中工作正常 - 事实上,像这样的一些阻塞是必要的,因此主控制台线程在异步工作完成之前不会退出 - 但它会在其他上下文中死锁,包括 WinForms。

它死锁的原因是await默认捕获上下文,并使用该上下文继续执行async方法。因此,当您await httpClient...代码运行时,它会捕获当前上下文,然后返回未完成的任务。然后,堆栈中更上方的调用代码将阻塞,等待该任务完成。

对于 WinForms UI 线程,该上下文是一个WinFormsSynchronizationContext,它始终在 UI 线程上执行代码。因此,当SendAsync完成时,它将恢复在 UI 线程上执行async方法。但是,该 UI 线程被阻止,等待任务完成。在 UI 线程释放之前,任务无法完成,因此出现死锁。

修复此死锁的正确方法是删除异步同步反模式。换句话说,将Wait()ResultGetAwaiter().GetResult()替换为await

最新更新