【问题标题】:Deadlock while logging http requests into main UI C#将 http 请求记录到主 UI C# 时出现死锁
【发布时间】:2018-08-10 23:01:31
【问题描述】:

在我的 WinForms 应用程序中,我想在它与 webApi 交互时查看原始请求/响应数据。我需要在 UI RichTextBoxes 中显示请求/响应。为此,我将 HttpClient 设置如下:

        private HttpClient client;
        private void CreateService()
        {
            client = new HttpClient(new LoggingHandler(new HttpClientHandler(), this))
            {
                BaseAddress = new Uri(this._URI)
            };
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
        }

我的日志处理程序:

    public class LoggingHandler : DelegatingHandler
    {
        private SimplySignRestClientAsync _client;

        public LoggingHandler(HttpMessageHandler innerHandler, SimplySignRestClientAsync client) : base(innerHandler)
        {
            this._client = client;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            string p_Request = "";
            if (_client._updateRequest != null)
            {
                p_Request = request.ToString();
                if (request.Content != null)
                {
                    p_Request += "\n\nContent:\n" + FormatJsonString(await request.Content.ReadAsStringAsync());    
                }

                //_client._request.Text = p_Request; << this works but causes a cross-thread exception in debug only
                SetText(p_Request); << This deadlocks the UI
            }

            return response;
        }

        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            if (_client._request.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                _client._response.Invoke(d, new object[] { text });
            }
            else
            {
                _client._request.Text = text;
            }
        }
    }

如果我尝试直接访问richtextbox,它仅在我执行应用程序而不调试时才有效。但是在调试过程中会出现跨线程异常。

这就是我调用 httpClient 的方式:

//This is how the original call gets initiated from UI
private void uiLogin_Click(object sender, EventArgs e)
{
   Agent resp = Client.Login(new Credentials(uiUsername.Text, uiPassword.Text));
   uiAuthToken.Text = Client.getAuthToken();
}

//this method has to stay synchronous, but it in turn calls an async method below
public Agent Login(Credentials loginInfo)
{
    var task = Task.Run(async () => await LoginAsync(loginInfo));
    return task.Result.Content;  <<< this is where the application stops if i hit pause during debug when deadlock happens
}

//
async public Task<RestResponse<Agent>> LoginAsync(Credentials loginInfo)
{
    HttpResponseMessage response = await client.PostAsJsonAsync(this._URI + "api/users/login", loginInfo);

    var content = await response.Content.ReadAsStringAsync();
    RestResponse<Agent> respAgent respAgent = JsonConvert.DeserializeObject<RestResponse<Agent>>(content);

    return respAgent;
}

据我了解,我的“返回 task.Result.Content”会阻塞 UI 线程和“_client._response.Invoke(d, new object[] { text });”正在等待解除阻塞导致死锁。但我也不确定如何让调用等待。

【问题讨论】:

标签: c# multithreading winforms async-await


【解决方案1】:

如果您想使用适当的异步构造来执行此操作,则需要使入口点(您的 Click 事件处理程序)异步,并且永远不要阻塞异步代码,因为它会导致您面临的死锁。

//This is how the original call gets initiated from UI
private async void uiLogin_Click(object sender, EventArgs e)
{
   Agent resp = await Client.Login(new Credentials(uiUsername.Text, uiPassword.Text));
   uiAuthToken.Text = Client.getAuthToken();
}

public async Task<Agent> Login(Credentials loginInfo)
{
    var result = await LoginAsync(loginInfo);
    return result.Content;
}

【讨论】:

  • Login(Credentials loginInfo) 函数必须保持同步
  • @AnKing 为什么?您无法使用同步方法正确地做到这一点
  • 因为我在 testApp 中使用 req/resp 输出。主应用程序正在使用所有同步调用。我也想在测试应用中模仿同步调用
  • @AnKing 你大概连接到在线服务来处理登录,是吗?这意味着您有两个选择。 A] 您异步处理登录,或 B] 在等待在线服务响应时阻塞 UI 线程(也就是冻结整个应用程序)。
猜你喜欢
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-13
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
  • 2015-12-29
相关资源
最近更新 更多