Увеличит ли многопоточность производительность метода в серваке WCF? - PullRequest
3 голосов
/ 05 ноября 2010

У меня есть служба WCF, размещенная в IIS6.Важная часть метода выглядит следующим образом:

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 ]

3 голосов
/ 05 ноября 2010

Во-первых, я хочу отметить, что компонент 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 и WaitOne может возникнуть очень тонкое состояние гонки.

Кстати, я должен отметить, что TPL доступен в .NET 3.5 как часть Reactive Extensions download.

1 Я подозреваю, что одна из двух упомянутых мной проблем будет в реальности.

2 голосов
/ 05 ноября 2010

Как правило, я бы сказал, что это может помочь - однако, это может вызывать беспокойство:

Кроме того, если этот метод вызывается одновременно более чем одним клиентом, время ожидания истекает.Я даже видел, как это обрушило пул приложений.Обратите внимание, что в цикле выполняется звонок в AzMan.AzMan - неуправляемый COM-компонент.

Похоже, что компонент "AzMan" не является потокобезопасным.В таком случае не удастся эффективно выполнить многопоточность этой подпрограммы, поскольку она проводит большую часть своего времени в этой подпрограмме.

Если, однако, эта подпрограмма является поточно-ориентированной и не разделяетмногопоточность может повысить производительность.Однако это зависит от многих других проблем, включая нагрузку на саму машину (если все ядра используются достаточно хорошо, это может не помочь) и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...