【问题标题】:Thread-safe access to WCF channel对 WCF 通道的线程安全访问
【发布时间】:2014-01-29 14:58:24
【问题描述】:

我有一个 ASP.NET 应用程序,它使用“服务参考”来连接第三方/非现场支付处理器。 我从支付处理器下载的示例代码包括以下内容:

   public class SoapAPIUtilities{

      private static CustomerProfileWS.Service service = null;

      public static CustomerProfileWS.Service Service{
         get{
            if(service == null){
               service = new CustomerProfileWS.Service();
            }
            return service;
         }
      }   
   }

我生成CustomerProfileWS.Serviceautomatically using Visual Web Developer:它的自动生成实现是ServiceModel.ClientBase的子类的子类,它 MSDN 文档为“不保证任何实例成员都是线程安全的”。

要从 ASP.NET 页面使用此服务,我想我需要对服务进行线程安全的访问,而以上不是?

如果它不是线程安全的,那么让它成为线程安全的更好方法是什么:

  • 包装一个访问器类,该类在静态单例 (e.g. as shown here) 周围实现锁定?
  • 不要使用静态单例;而是根据需要创建多个/临时 CustomerProfileWS.Service 实例,作为需要它们的 ASP.NET 页面方法中的局部变量?

【问题讨论】:

    标签: asp.net wcf web-services thread-safety authorize.net


    【解决方案1】:

    使代理线程安全且单例不适合 Web 应用程序。

    当另一个线程正在使用代理通道时,它可能会出现故障。此外,您不希望每次操作都锁定通道,因为网站同时有许多请求,它们将被连续处理,等待长时间运行的任务,例如调用 web 服务。

    您需要为每个 ASP .NET 请求创建一个新的代理通道。这是一项昂贵的操作,因此您可能需要查看这篇文章以确保缓存创建成本高昂的部分:

    Performance Improvement for WCF Client Proxy Creation

    另一种方法是创建一个代理池,这样您就不必每次都创建一个新代理,而是在现有代理可用的情况下重用它。可以在这里找到一个例子:

    A Sample for WCF Client Proxy Pooling

    【讨论】:

    • +1 感谢您的参考。 My example of a wrapper around a singleton 尝试从故障中恢复,如果出现故障则重新创建单例。
    • 在这种情况下不起作用:Thread1 进行了 1 次调用。 Thread2 再次调用。 Thread1 调用失败,通道出现故障。 Thread2 无法继续操作,因为通道已被 Thread1 故障。即使您恢复了代理,您的两个网络请求(或同时拥有的多个)也会失败,因为其中一个有问题。
    • 我认为它会起作用?线程通过在 using 语句中实例化 Authorize 类来“进行调用”,例如using (Authorize auth = new Authorize()) { auth->IsAlive(); } 有一个“锁”(Monitor.Enter) 确保 Thread2 对 Authorize 构造函数的调用在 Thread1 处理其 Authorize 实例之前不会完成,从而使单例处于良好状态或失败状态。如果失败,则 Thread2 的 Authorize 构造函数在获取锁之后重新创建单例,然后返回给调用者并让调用者调用其他 Authorize 消息。
    • 如果代理频道被锁定 0.1 秒,并且您同时有 100 个用户对该页面的请求,则频道将在最后一次请求时被锁定 10 秒。根据您使用锁的方式,如果在 10 秒内无法访问资源,代码可能会失败。当我们谈论用户时,您也会失去可扩展性(您可能有 1000 个用户访问需要该代理通道的页面)。
    【解决方案2】:

    我会选择你建议的第二个选项...

    不要使用静态单例;而是创建多个/临时 CustomerProfileWS.Service 实例根据需要,作为局部变量在 ASP.NET 页面的方法需要它们吗?

    【讨论】:

    • 请告诉我你的理由?我对这种方法的担心是a)我不知道是否有必要b)我无法测试远程/服务器端是否正确处理来自同一客户端/商家/站点的并发请求c)因为他们不是这样做的它在他们的“示例程序”中可能不受支持。
    • 通过为每个正在处理的付款重新创建您的 CustomerProfileWS.Service 对象,您可以完全消除多线程场景。就此而言,我强烈反对在 ASP.NET 应用程序中启动多个线程。
    • 我没有启动线程。但是,如果网站有多个并发用户,那么不是同时存在多个 ASPX 页面实例(每个都有自己的线程)吗?
    • 就 IIS 而言,存在不止一个实例。但是,就您的代码而言,如果您自己不启动新线程,则一次只有一个请求会访问该代码块......
    • 如果有两个网页同时运行,那么它们可能都尝试访问同一个通道实例,如果通道是静态单例,如示例中所示(不是因为我是启动线程,但因为 ASP.NET 页面实例可以同时执行)。
    【解决方案3】:

    我会把它改成private static readonly CustomerProfileWS.Service service = new CustomerProfileWS.Service();

    这将以线程安全的方式创建您的单例。

    但是,如果多个线程访问 WCF 代理,一个线程可以关闭连接,而另一个线程正在运行。

    我认为每个 ASP.NET 请求的代理可能更适合这里。

    【讨论】:

    • 我的代码不需要显式打开或关闭通道,是吗?假设我的代码从未显式打开或关闭通道,那么通道是线程安全的吗?如果它接收到多个并发请求,它是否会隐式序列化请求?
    猜你喜欢
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 2020-09-07
    • 2011-12-26
    相关资源
    最近更新 更多