异步向主机发送数据时的服务器繁忙消息框

mar*_*rak 5 c# winforms

我构建了一个 Windows 窗体应用程序,它通过按钮单击事件将数据(使用 POST 调用)发送到 Web 服务。在生产中,有时用户会看到“服务器忙”消息框(如下所示)。

错误消息屏幕截图

我正在使用异步等待模式将消息发布到服务。下面是我的代码片段:

private void button1_Click(object sender, EventArgs e)
{
   SendDataToHost();
}

private async void SendDataToHost()
{
   //Get the data which needs to be sent to the host..

   var result= await PostData(data);
    //If the service fails to process this data for any reason, then
    // it returns the result object with succeeded as false    
    if (result != null && !result.Succeeded)
    {
        MessageBox.Show("Failed");
    }
    else
    {
        MessageBox.Show("Success");
    }
}

public async Task<Result> PostData(Data data)
{
    Result result = await PostAsync("cases", data);

    return result;
}
Run Code Online (Sandbox Code Playgroud)

我了解到“服务器繁忙”消息框正在填充,因为 UI 线程被阻止。但我想理解的是,使用 Async 和 Await 的目的是保持 UI 响应,但为什么 UI 线程被阻塞?

另外,我在网上研究并找到了一些解决方案,但我想完全了解他们是否能解决问题?由于这是一个生产问题,我无法在开发环境中重现,我想确保更改能够解决该问题。

我发现的两个选项是:

1)使用ConfigureWait(false). 根据我的理解,这将确保“POST”调用不会在 UI 上下文中发生。

public async Task<Result> PostData(Data data)
{
    Result result = await PostAsync("cases",data).ConfigureAwait(false);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

2)在新任务中调用Post方法并等待该任务。

private async void SendDataToHost()
{
   //Get the data which needs to be sent to the host..

   **var result= await Task.Run(()=>PostData(data));**

    //If the service fails to process this data for any reason, then
    // it returns the result object with succeeded as false    
    if (result != null && !result.Succeeded)
    {
        MessageBox.Show("Failed");
    }
    else
    {
        MessageBox.Show("Success");
    }   
}
Run Code Online (Sandbox Code Playgroud)

我想知道解决这个问题的方向是否正确,或者我还需要探索其他选择吗?

Han*_*ant 4

这是一个非常标准的消息框,很多程序都可以显示它。 这是QuickBooks 执行此操作的一个示例,谷歌搜索“服务器繁忙,此操作无法完成”将为您提供更多示例。他们给出的解决问题的建议很少有用。

Visual Studio 是也具有此功能的应用程序的一个很好的示例。然而,它不使用标准消息框,而是定制了报告事故的方式。检查这个问答

您的问题中没有足够的线索来猜测“服务器”可能是什么以及它为何行为不当。它当然不是一个“网络服务”,有人在向您出售大量马毛,对于一个没有人真正理解的非常古老的代码库来说,这并不罕见。所以我必须通过解释管道来解决这个问题。消息框由操作系统内置的 COM 基础结构显示。当您在 COM 服务器上进行调用并且该调用需要封送到另一个线程或另一个进程时,它就会参与您的程序。PostAsync() 可能就是该调用,尽管它没有此类方法的非常典型的名称。也许它被某人包装以使其异步。

COM 基础结构提供的一项服务是监视此类调用。确保它在合理的时间内完成。其中“合理”为 60 秒,大约是程序无响应时人们愿意等待的时间。此类调用花费太长时间是一个非常严重的可用性问题,它有能力使程序的用户界面挂起并让用户失去对程序的控制。

消息框的目的是让他知道出现了问题,并且不是您的程序有问题。然而,“切换到”按钮很少能达到预期的效果,现在也不会了。在过去,当 OLE 仍然流行时,这种事故更为常见,它将帮助用户单击消息框或以其他方式从服务器获取诊断,以找出它不合作的原因。

如今,更有可能的原因是消防水带问题。以远高于服务器执行速度的速度向服务器发送请求。这是您追求异步代码的可能原因之一,您之前可能注意到服务器完成其工作的速度不是很快。如果这导致您的用户界面无响应,那没关系,但这并不能解决核心问题。事实上,它会让事情变得更糟,你的程序现在可以更快地进行这些调用,从而使消防水带问题变得更糟。当服务器备份过多时,最终会触发 COM 诊断。

对此没有干净的解决方案,您只需放慢速度,这样您的程序就不会压垮服务器。从技术上讲,可以抑制消息框,使其永远不会出现,这通常是程序员首先寻找的。您可以通过编写自己的 IMessageFilter 接口实现并通过 pinvoking CoRegisterMessageFilter() 告诉 COM 管道来实现此目的。您将在这篇 MSDN 文章中找到 C# 样板代码。

请注意,Register() 调用必须由创建 COM 对象的同一线程进行,如果组件被包装或在非 STA 工作线程上创建它,则这不一定很简单。您将找到为此类组件创建一个好客之家的代码,因此您可以在本文中可靠地进行 Register() 调用。还为您提供了一种创建自己的队列并避免异步调用的方法,这样您就不必依赖 COM 管道。