3

我有一个托管在 IIS6 中的 WCF 服务。该方法的重要部分如下所示:

public MyUser[] GetUsers(string appName, string[] names)
{
    List<User> users = new List<User>();
    foreach (string user in names)
    {
      MembershipUser mu = this.ADAMProvider.GetUser(user, false);  //Unmanaged call to AzMan
      if (mu != null)
      {
        users.Add(MyUser.CreateFrom(mu);
      }
    }
    return users.ToArray();
}

当使用大量用户名(超过 100 个左右)调用此方法时,该方法的性能很差。返回可能需要一分钟多的时间。此外,如果多个客户端同时调用此方法,则会超时。我什至看到它降低了应用程序池。请注意,在循环中调用了 AzMan。AzMan 是非托管 COM 组件。

为了提高性能,我正在考虑采用多线程方法。.NET 4 不是一个选项,因此 Parallel.For 不是一个选项,但在 3.5 中进行等效操作是。

我的问题是创建一堆线程(然后在返回之前等待所有线程)实际上会提高性能吗?在 IIS6 托管的 WCF 服务中执行此操作是否存在危险?

4

2 回答 2

3

首先,我已经指出 COM 组件可能是一个问题,这取决于它的公寓状态。单线程单元对象只能在一个线程中运行。STA 对象上会发生一个自动编组操作,该操作将有效地序列化对其的所有调用,因此无论您多么努力,都可能无法从中获得任何并行化。即使它是一个 MTA 对象,GetUser如果它的设计不是线程安全的,该方法也可能存在问题。

但是假设这些都不是问题1我会使用ThreadPool而不是创建一堆线程来执行此操作。这是它可能的样子。

public MyUser[] GetUsers(string appName, string[] names) 
{ 
  int count = 1; // Holds the number of pending work items.
  var finished = new ManualResetEvent(false); // Used to wait for all work items to complete.
  var users = new List<User>(); 
  foreach (string user in names) 
  {
    Interlocked.Increment(ref count); // Indicate that we have another work item.
    ThreadPool.QueueUserWorkItem(
      (state) =>
      { 
        try
        {
          MembershipUser mu = this.ADAMProvider.GetUser(user, false); 
          if (mu != null) 
          { 
            lock (users)
            {
              users.Add(MyUser.CreateFrom(mu); 
            }
          } 
        }
        finally
        {
          // Signal the event if this is the last work item.
          if (Interlocked.Decrement(ref count) == 0) finished.Set();
        }
      });
  } 
  // Signal the event if this is the last work item.
  if (Interlocked.Decrement(ref count) == 0) finished.Set();
  // Wait for all work items to complete.
  finished.WaitOne();
  return users.ToArray(); 
}

关于我上面使用的模式的一个令人困惑的事情是,它将主线程(排队工作的线程)视为另一个工作项。这就是为什么您在循环结束时看到检查和信号代码的原因。Set没有它,在andWaitOne调用之间可能会发生非常微妙的竞争条件。

顺便说一句,我应该指出 TPL 在 .NET 3.5 中作为Reactive Extensions下载的一部分提供。

1我怀疑我提到的两个问题之一将在现实中发挥作用。

于 2010-11-05T16:05:50.383 回答
2

通常,我会说这可能会有所帮助-但是,这将引起关注:

此外,如果多个客户端同时调用此方法,则会超时。我什至看到它降低了应用程序池。请注意,在循环中调用了 AzMan。AzMan 是非托管 COM 组件。

这听起来像“AzMan”组件不是线程安全的。如果是这种情况,就不可能有效地对这个例程进行多线程处理,因为它大部分时间都花在那个例程上。

但是,如果该例程是线程安全的,并且不共享状态,那么多线程可能会提高性能。但是,这取决于许多其他问题,包括机器本身的工作负载(如果所有内核都得到很好的利用,它可能无济于事)等。

于 2010-11-05T15:53:13.423 回答