29

Silverlight WCF 服务代理生命周期的主题对我来说不是很清楚。我在这里阅读了各种材料、资源和答案,但我仍然不完全理解使用它们的最佳方式。

我目前在 Silverlight 4.0 中使用自定义二进制绑定。

在 silverlight 中创建代理是一项昂贵的操作吗?我们应该尝试在代码中共享代理实例还是创建新的更好?如果我们共享以防多个线程访问它,我们是否应该进行锁定?

由于代理上的错误会使代理的状态出错,我认为共享代理不是一个好主意,但我读过创建是昂贵的,所以它不是 100% 清楚在这里做什么。

并且关闭 - silverlight WCF 服务客户端仅提供 CloseAsync 方法。此外,代理需要在关闭时使用某些逻辑(如果它们出现故障,我们应该调用在 Silverlight 中同步的 Abort(),如果不是,我们应该调用非同步的 CloseAsync 或什么?)。

在许多来自 MS 代理的官方 Silverlight 样本中,都没有关闭,这仅仅是材料的缺陷还是预期的方法?

主题对我来说非常重要,我想清楚地了解我目前没有的所有应该考虑的事情。

(我确实看到了这个问题What is the proper life-cycle of a WCF service client proxy in Silverlight 3?看起来很接近我,但我不能说我对答案的质量感到满意)

我真的很想看到使用、创建、关闭等 WCF 代理的示例代码,最重要的是解释了为什么这是最好的方法。我还认为(目前认为)由于问题的性质,应该有一个单一的、通用的最佳实践/模式——在 Silverlight 中使用(创建、重用、关闭)WCF 代理的方法。

4

2 回答 2

8

简介:我相信最佳实践是在您即将使用 Web 服务客户端时实例化它,然后让它超出范围并收集垃圾。这反映在您看到的来自 Microsoft 的示例中。理由如下...

完整:我发现的过程的最佳完整描述是如何:从 Silverlight 访问服务。此处的示例显示了实例化 Web 服务客户端并允许其超出范围(无需关闭它)的典型模式。Web 服务客户端从 ClientBase 继承,后者有一个 Finalize 方法,当对象被垃圾回收时,该方法应在必要时释放任何非托管资源。

我有大量使用 Web 服务的经验,我使用代理并在使用前将它们实例化,然后允许它们被垃圾收集。我从来没有遇到过这种方法的问题。我在Wenlong Dong 的博客上看到说创建代理很昂贵,但即使他说 .NET 3.5 中的性能也有所提高(也许从那以后又有所提高?)。我可以告诉您的是,性能是一个相对术语,除非您检索的数据大小不那么重要,否则在序列化/反序列化和传输方面将花费比创建连接更多的时间。这当然是我的经验,你最好先在这些领域进行优化。

最后,由于我认为到目前为止我的意见可能不够,我写了一个快速测试。我使用 Visual Web Developer 2010 Express 提供的模板创建了启用 Silverlight 的 Web 服务(使用名为 的默认 void 方法DoWork())。然后在我的示例 Silverlight 客户端中,我使用以下代码调用它:

int counter=0;
public void Test()
{
    ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
    client.DoWorkCompleted += (obj, args) => 
    { 
        counter++;
        if (counter > 9999)
        {
            for(int j=0;j<10;j++) GC.Collect();
            System.Windows.MessageBox.Show("Completed");
        }
    };
    client.DoWorkAsync();
}

for(int i=0;i<10000;i++) Test();然后我使用并启动了应用程序调用了测试方法。加载应用程序并完成 Web 服务调用(全部 10,000 个)花费了 20 多秒。在进行 Web 服务调用时,我看到该进程的内存使用量跃升至 150MB 以上,但一旦调用完成GC.Collect()并被调用,内存使用量下降到不到该数量的一半。它远不是一个完美的测试,它似乎向我确认没有内存泄漏,或者它可以忽略不计(考虑到调用 10,000 个 Web 服务调用都使用单独的客户端实例可能并不常见)。此外,它是一个比保留代理对象并不必担心它会出现故障并不得不重新打开它的模型要简单得多。

测试方法的理由: 我的测试集中在两个潜在的问题上。一个是内存泄漏,另一个是创建和销毁对象所花费的处理器时间。我的建议是遵循提供课程的公司(Microsoft)提供的示例是安全的。如果您担心网络效率,那么您应该对我的示例没有问题,因为正确创建/处置这些对象不会影响网络延迟。如果花费 99% 的时间是网络时间,那么优化 1% 的理论改进可能在开发时间方面是浪费的(假设甚至可以获得好处,我相信我的测试清楚地表明几乎没有/没有任何)。是的,网络调用是本地的,也就是说,在 10,000 次服务调用过程中,等待对象的时间只有大约 20 秒。这表示每个服务调用花费在创建对象上的时间约为 2 毫秒。关于调用 Dispose 的必要性,我并不是要暗示你不应该调用它,只是它似乎没有必要。如果你忘记了(或者只是选择不忘记),我的测试让我相信在 Finalize 中为这些对象调用了 Dispose。即便如此,自己调用 Dispose 可能会更有效,但效果仍然可以忽略不计。对于大多数软件开发而言,提出更有效的算法和数据结构比解决此类问题(除非存在严重的内存泄漏)获得更多收益。如果您需要更高的效率,那么也许您不应该使用 Web 服务,因为有比基于 XML 的系统更有效的数据传输选项。

于 2011-08-24T02:21:44.573 回答
0

相对于调用的往返,代理创建并不昂贵。我看过评论说CloseAsync在您调用其他异步方法后立即调用,但这似乎是有缺陷的。理论上,关闭会进入挂起状态,并在您的其他异步调用结束后发生。在实践中,我看到调用以异常快的速度结束并出现故障,然后CloseAsync由于通道已经出现故障而导致自身故障。

我所做的是创建一个泛型Caller<TResult>类,该类具有许多CallAsync接受参数和类型回调的泛型方法重载Action<TResult>。在幕后,他们Caller将连接XxxCompleted事件并检查完成的原因是否是由于错误。如果错误,它将Abort是通道。如果没有错误,它将调用CloseAsync然后调用回调。这可以防止“愚蠢的”错误,例如尝试使用处于故障状态的通道。

以上所有都假设了一个创建-代理-进行-调用-丢弃-使用-代理模型。如果您必须快速连续拨打许多电话,您可以采用这种方法来计算正在进行的电话的数量,并在最后一个电话完成后,执行关闭或中止。

每次调用或每组相关调用的代理创建开销可能很少会成为问题。只有当开销太高时,我才会追求代理缓存或重用策略。请记住,您持有资源的时间越长,失败的可能性就越大。至少根据我的经验,生命周期越短,用户感知的性能就越好,因为你打断他/她的次数越少,你就不需要昂贵的费用来维护重试逻辑(或者至少更少),而且你通过准确地知道何时摆脱它们,您将更有信心不会泄漏内存或资源。

关于您关于示例未结束的问题......好吧,这些是示例,通常是一维且不完整的,因为它们说明了一般用途,而不是关于您在整个应用程序生命周期中需要做什么的所有细节。

于 2011-08-27T01:00:50.920 回答