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

发布于 2024-10-01 00:16:24 字数 728 浏览 8 评论 0原文

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

I have a WCF service that is hosted in IIS6. The important part of the method looks like this:

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();
}

The performance of this method is very poor when it is called with a large array of user names (over 100 or so). It can take over a minute to return. Also, if this method is called concurrently by more than one client it will time out. I have even seen it bring down the app pool. Please note that in the loop a call to AzMan is being made. AzMan is unmanaged COM component.

To increase performace I am considering a multi-threaded approach. .NET 4 is not an option so Parallel.For is not an option but doing the equivalant in 3.5 is.

My question is will creating a bunch of threads (then waiting for all before returning) actually increase performace? Is there an danger in doing this in an IIS6 hosted WCF service?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无风消散 2024-10-08 00:16:24

首先,我指出 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 调用之间可能会出现非常微妙的竞争条件。

顺便说一句,我应该指出,TPL 在 .NET 3.5 中作为

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

First, I have point out that the COM component could be a problem depending on its apartment state. Single-threaded apartment objects can only run in one thread. There is an automatic marshalling operation that occurs on STA objects that would effectively serialize all calls to it so no matter how hard you try you may not get any parallelization out of it. Even if it were an MTA object there might be a problem with the GetUser method if it is not designed to be thread-safe.

But assuming none of this is a problem1 I would use the ThreadPool instead of creating a bunch of threads to do this. Here is what it might look like.

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(); 
}

One confusing thing about the pattern I used above is that it treats the main thread (the one queueing the work) as if it were another work item. That is why you see the check and signal code at the end of the loop. Without it there is an a very subtle race condition that could occur between the Set and WaitOne calls.

By the way, I should point out that the TPL is available in .NET 3.5 as part of the Reactive Extensions download.

1I suspect one of the two problems I mentioned will be in play in reality.

你另情深 2024-10-08 00:16:24

通常,我会说这可能会有所帮助 - 但是,这会令人担忧:

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

这听起来像“AzMan”组件不是线程安全的。如果是这种情况,则无法有效地多线程该例程,因为它的大部分时间都花在该例程上。

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

Normally, I'd say this may help - however, this would be of concern:

Also, if this method is called concurrently byu more than one client it will time out. I have even seen it bring down the app pool. Please note that in the loop a call to AzMan is bening made. AzMan is unmanaged COM component.

This sounds like the "AzMan" component is not thread safe. If that's the case, it will not be possible to multi-thread this routine effectively, as it is spending most of its time in that routine.

If, however, that routine is thread safe, and does not share state, multithreading might improve the performance. However, it depends on a lot of other issues, including the workload of the machine itself (if all cores are being fairly well utilized, it may not help), etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文