【问题标题】:Async WCF service call blocking UI thread异步 WCF 服务调用阻塞 UI 线程
【发布时间】:2015-04-18 05:34:24
【问题描述】:

我们遇到了来自 WPF 应用程序的后台调用问题。看起来,在较旧的计算机上(我们最常在具有 2GB RAM 左右的 Windows 7 计算机上看到它)我们的 Web 服务调用 阻塞 UI 直到它完成。特别是对于一个呼叫,这是一个巨大的问题,因为它可能需要五分钟才能完成。较新的计算机似乎可以很好地处理它。

我们不关心通话花费的时间;我们只关心它不会阻塞 UI。我没有看到我们做错了什么。

是我们做错了什么,还是 Windows 7 在 2GB RAM 上的问题?有没有办法在这样的机器上解决它?

我们是否必须达到编写自定义TaskScheduler 的水平以确保不使用 UI 线程?我当然希望不会。

非常感谢任何输入。提前致谢。请参阅下面的代码示例。

DownloadBusinessEntityAsync 是我们的应用程序调用的方法:

    #region DownloadBusinessEntity
    public async Task<BusinessEntity> DownloadBusinessEntityAsync(string businessEnityId)
    {
        BusinessEntity ret = new BusinessEntity();

        var client = new DownloadContext();

        try
        {
            Func<AsyncCallback, object, IAsyncResult> begin = (callback, state) => client.BeginDownloadBusinessEntity(businessEnityId, callback, state);
            Func<IAsyncResult, BusinessEntity> end = client.EndDownloadBusinessEntity;

            // ***THIS TAKES FIVE MINUTES TO FINISH***
            ret = await Task<BusinessEntity>.Factory.FromAsync(begin, end, null);

            if (client.State != CommunicationState.Faulted)
                client.Close();
            else
                client.Abort();
        }
        catch (Exception ex)
        {
            client.Abort();
        }

        return ret;
    }
    #endregion

DownloadContext 是 WCF 客户端:

public partial class DownloadContext : ClientBase<IDownloadService>, IDownloadService, IDownloadContext, IDisposable
{
    public BusinessEntity DownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes)
    {
        return base.Channel.DownloadBusinessEntity(agencyId, activeHashCodes);
    }

    public IAsyncResult BeginDownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes, AsyncCallback callback, object asyncState)
    {
        return base.Channel.BeginDownloadBusinessEntity(agencyId, activeHashCodes, callback, asyncState);
    }

    public BusinessEntity EndDownloadBusinessEntity(IAsyncResult result)
    {
        return base.Channel.EndDownloadBusinessEntity(result);
    }
}

IDownloadService 是 WCF 客户端实现的合同。

[ServiceContract(Namespace = "http://namespace.com/services/v1")]
public partial interface IDownloadService
{
    [OperationContract(ProtectionLevel=ProtectionLevel.Sign, Action="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntity", ReplyAction="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntityResponse")]
    BusinessEntity DownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes);

    [OperationContract(AsyncPattern=true, ProtectionLevel=ProtectionLevel.Sign, Action="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntity", ReplyAction="http://namespace.com/services/v1/IDownloadService/DownloadBusinessEntityResponse")]
    IAsyncResult BeginDownloadBusinessEntity(string agencyId, IEnumerable<int> activeHashCodes, AsyncCallback callback, object asyncState);

    BusinessEntity EndDownloadBusinessEntity(IAsyncResult result);

}

【问题讨论】:

    标签: wcf asynchronous


    【解决方案1】:

    Microsoft 的客户端 HTTP 连接实现存在一个已知问题(HttpWebRequest 和朋友):它们同步执行一些工作,包括 DNS 解析和(我相信)代理检测。即使对于异步请求,这项工作也始终同步完成,并且可能需要一些时间,具体取决于您的网络配置。

    我不确定 WCF 客户端是否共享此错误,但很有可能。最好的解决方法是按照usr's answer 将其包装在Task.Run 中。

    【讨论】:

    • 感谢您的回复。 DNS解析需要五分钟吗?还是您的意思是 DNS 解析后的所有内容也将是同步的?此外,即使是最新版本的 Windows 也存在这个已知问题?当我写信给@usr 时,我会尝试恢复到Task.Run,​​但我相信这就是我们之前所拥有的。对客户端 HTTP 连接使用不同的方法是否也能解决这个问题?
    • 这是 .NET 框架中的问题,与 Windows 版本无关。 DNS(和代理)解析通常不会花费那么长时间,除非您的网络配置错误。实际的 WCF 调用是异步执行的。 AFAIK,所有基于 .NET 的 HTTP 客户端都有这个问题。
    【解决方案2】:

    如果您只是不想阻止用户界面,则将该工作移至后台线程。实现您的目标不需要所有异步 WCF 的东西。比如:

    await Task.Run(() => DownloadBusinessEntityAsync(...));
    

    如果你愿意,你可以让DownloadBusinessEntityAsync同步。

    【讨论】:

    • 其实我们之前也是这样,相信也有同样的问题。我们已经在这个问题上工作了很长时间,所以也许有些事情已经改变了。 @Stephen Cleary 的回答听起来很像我们所看到的。
    • 这永远不会出现任何阻塞。 DNS 解析将在线程池上进行。用 Thread.Sleep(1000000) 替换方法的主体,然后自己看看。
    • 将 Task.Run 放入 DownloadBusinessEntityAsync 是否能够完成同样的事情(线程池中的 DNS)?该异步调用是我们的“异步层”的一部分,它将应用程序与实际调用分开。如果可能的话,我想避免让应用程序执行 Task.Run。再次感谢您的帮助!
    • 只要你把之前阻塞的东西放在线程池上,阻塞就不会影响 UI 线程。所以,是的,那会奏效。只需将所有内容包装在该方法中或使用单独的包装方法即可。
    • 据我所知,这似乎有效。似乎还有其他事情发生,但似乎是在 WCF 调用之前。如果没有,我会发布。再次感谢!
    猜你喜欢
    • 2017-07-07
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多