Увеличит ли многопоточность производительность метода в серваке WCF?
У меня есть служба 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 ответа
Во-первых, я должен отметить, что компонент 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.
1Я подозреваю, что одна из двух упомянутых мной проблем будет в реальности.
Обычно, я бы сказал, что это может помочь - однако, это может быть проблемой:
Кроме того, если этот метод вызывается одновременно более чем одним клиентом, время ожидания истекает. Я даже видел, как это обрушило пул приложений. Обратите внимание, что в цикле выполняется вызов AzMan. AzMan - неуправляемый COM-компонент.
Похоже, что компонент "AzMan" не является потокобезопасным. Если это так, то не удастся эффективно выполнить многопоточность этой подпрограммы, так как она проводит большую часть своего времени в этой подпрограмме.
Однако, если эта подпрограмма является поточно-ориентированной и не разделяет состояние, многопоточность может повысить производительность. Однако это зависит от многих других проблем, включая нагрузку на саму машину (если все ядра используются достаточно хорошо, это может не помочь) и т. Д.