我构建了一个Windows窗体应用程序,它通过按钮单击事件将数据(使用POST调用)发送到Web服务。在生产中,有时用户会看到“服务器忙”消息框(如下所示)。
我使用Async Await模式将消息发布到服务。以下是我的代码段:
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;
}
我理解正在填充“服务器忙”消息框,因为UI线程被阻止。但我想要了解的是,使用Async和Await的目的是保持UI响应,但为什么UI线程被阻止?
另外,我在线研究并找到了一些解决方案,但我想完全理解他们是否会解决这个问题?由于这是一个生产问题,我无法在开发环境中重现,我想确保更改将解决问题。
我找到的两个选项是:
1)使用ConfigureWait(false)
。根据我的理解,这将确保在UI上下文中不会发生“POST”调用。
public async Task<Result> PostData(Data data)
{
Result result = await PostAsync("cases",data).ConfigureAwait(false);
return result;
}
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");
}
}
我想知道我是否朝着正确的方向前进,以解决这个问题 或者我还需要探索其他选择吗?
答案 0 :(得分:3)
这是一个非常标准的消息框,很多程序都可以显示它。 Here is an example QuickBooks这样做,谷歌搜索“服务器忙这个动作无法完成”将给你更多的例子。他们提出的解决问题的建议很少有用。
Visual Studio也是具有此功能的应用程序的一个很好的示例。但它不使用标准消息框,而是自定义报告事故的方式。检查this Q+A。
你的问题中没有足够的暗示来猜测“服务器”可能是什么以及它为什么行为不端。它肯定不是一个“网络服务”,有人卖给你一堆马毛,对于一个没有人真正理解的非常古老的代码库来说并不罕见。因此,我将不得不通过解释管道来解决这个问题。消息框由OS内置的COM基础结构显示。当您在COM服务器上进行调用时,它会参与您的程序,并且该调用需要被编组到另一个线程或另一个进程。 PostAsync()可能就是那个调用,尽管它没有这种方法的非常典型的名称。也许它被某人包裹起来让它变得异步。
COM基础架构提供的一项服务是监控此类呼叫。确保在合理的时间内完成。在“合理”是60秒的情况下,只要人们愿意等待程序变得无响应时。这样的呼叫花费太长时间是一个相当严重的可用性问题,它有一个诀窍,使程序的用户界面挂起,并让用户失去对程序的控制。
消息框的目的是让他知道某些东西是错过的,并且不是你的程序有问题。然而,“切换到”按钮很少具有预期效果,现在不再具有预期效果。在OL仍然流行的旧时代,这种事故更为常见,它将帮助用户点击消息框或以其他方式从服务器获取诊断,以找出它不合作的原因。
今天,更可能的原因是 fire-hose 问题。使用远高于其可执行速率的请求对服务器进行攻击。这是你追求异步代码的可能原因之一,你可能在之前注意到服务器不是很快就能完成它的工作。如果这会使您的UI无响应,但这并不能解决核心问题,这是可以的。实际上它可以使情况变得更糟,你的程序现在可以更快地调用这些调用,从而使firehose问题变得更糟。当服务器备份太多时,最终会触发COM诊断。
没有干净的解决方案,你只需要慢一点,这样你的程序就不会压倒服务器。技术上可以抑制消息框,使其永远不会出现,通常是程序员首先查找的内容。您可以通过编写自己的IMessageFilter接口实现来完成此操作,并通过对CoRegisterMessageFilter()进行调整来告诉COM管道。您将在this MSDN article中找到样板C#代码。
请注意,Register()调用必须由创建COM对象的同一个线程进行,如果组件被包装或在非STA工作线程上创建它,则这不一定很简单。你会找到为这样一个组件创建一个好客的家的代码,这样你就可以可靠地调用Register()in this post。还为您提供了一种创建自己的队列并避免异步调用的方法,因此您不依赖于COM管道。