簡體   English   中英

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

[英]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()ResultGetAwaiter().GetResult() 換句話說,您的代碼是同步異步。 這在Console應用程序中工作正常 - 事實上,這樣的一些阻塞是必要的,因此主控制台線程在異步工作完成之前不會退出 - 但它將在其他上下文中死鎖,包括WinForms。

它死鎖的原因是因為await默認捕獲一個上下文,並使用該上下文繼續執行async方法。 因此,當您await httpClient...代碼運行時,它會捕獲當前上下文,然后返回一個不完整的任務。 然后,您的調用代碼將進一步阻塞,等待該任務完成。

對於WinForms UI線程,該上下文是WinFormsSynchronizationContext ,它始終在UI線程上執行代碼。 因此,當SendAsync完成時,它將繼續在UI線程上執行async方法。 但是,該UI線程被阻止,等待任務完成。 在UI線程空閑之前,任務無法完成,因此您遇到了死鎖。

修復此死鎖的正確方法是刪除sync-over-async反模式 換言之,代替Wait()ResultGetAwaiter().GetResult()await

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM