多线程是否会提高WCF服务中方法的性能?

时间:2010-11-05 15:48:27

标签: multithreading wcf iis-6 azman

我有一个在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服务中执行此操作是否存在危险?

2 个答案:

答案 0 :(得分: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(); 
}

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

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

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

答案 1 :(得分:2)

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

  

此外,如果多个客户端同时调用此方法,它将超时。我甚至看到它打倒了应用程序池。请注意,在循环中,对AzMan的调用正在进行中。 AzMan是非托管COM组件。

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

但是,如果该例程是线程安全的,并且不共享状态,则多线程可能提高性能。但是,它取决于许多其他问题,包括机器本身的工作量(如果所有核心都得到了很好的利用,它可能没有用),等等。