Возможны ли проблемы параллелизма при использовании атрибута Поведение службы WCF, установленного в ConcurrencyMode.Multiple и InstanceContextMode.PerCall? - PullRequest
23 голосов
/ 30 марта 2010

У нас есть служба WCF, которая выполняет множество транзакционных вызовов NHibernate. Время от времени мы видели тайм-ауты SQL, даже если вызовы обновляли разные строки, а таблицы были настроены на блокировку на уровне строк.

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

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]

Недавно мы изменили режим параллелизма на ConcurrencyMode.Single и еще не сталкивались с какими-либо проблемами, но эту ошибку было очень трудно воспроизвести (если у кого-нибудь есть мысли по поводу удаления такой ошибки, дайте мне знать!) .

В любом случае, все это приводит меня к моему вопросу: не должен ли InstanceContextMode of PerCall обеспечивать безопасность потоков в службе, даже если для параметра ConcurrencyMode задано многократное значение? Как было бы возможно, чтобы два вызова обслуживались одним и тем же экземпляром службы?

Спасибо!

Ответы [ 4 ]

24 голосов
/ 30 марта 2010

Единственный способ иметь два разных клиента WCF, то есть прокси, ссылающихся на один и тот же экземпляр службы WCF, - это использовать InstanceContextMode=InstanceContextMode.Single. Это плохой выбор, если масштабирование является проблемой, поэтому вы можете использовать PerCall, если можете.

При использовании PerCall, каждый CALL для службы WCF получает свой собственный экземпляр службы WCF . Нет общего доступа к экземпляру службы, но это не означает, что они не используют одно и то же внутреннее хранилище (например, базу данных, память, файл и т. Д.). Просто помните, PerCall позволяет каждому звонить для одновременного доступа к вашей службе WCF.

Параметр ConcurrencyMode контролирует модель потоков самой службы. Значение Single ограничивает запуск всех экземпляров службы WCF в одном потоке. Таким образом, если у вас есть несколько клиентов, подключающихся одновременно, они будут выполняться только по одному на стороне службы WCF. В этом случае вы используете WCF для обеспечения синхронизации. Как вы видели, он будет работать нормально, но думайте об этом как о наличии только макроуровневого контроля над синхронизацией - каждый вызов службы WCF будет выполняться полностью до того, как может быть выполнен следующий вызов.

Установка ConcurrencyMode на Multiple, однако, позволит всем экземплярам службы WCF выполняться одновременно. В этом случае вы несете ответственность за обеспечение необходимой синхронизации . Думайте об этом, как о контроле синхронизации на микроуровне, поскольку вы можете синхронизировать только те части каждого вызова, которые должны быть синхронизированы.

Надеюсь, я объяснил это достаточно хорошо, но вот фрагмент документации MSDN для ConcurrencyMode на всякий случай:

Установка параметра ConcurrencyMode на Single указывает системе на ограничение экземпляры службы в один поток исполнения за один раз, что освобождает Вы имеете дело с потоками проблемы. Значение Multiple означает, что сервисные объекты могут быть выполнены несколько потоков одновременно. В В этом случае вы должны обеспечить поток безопасность.

EDIT

Вы спросили

Есть ли какое-либо увеличение производительности при использовании PerCall по сравнению с Single при использовании ConcurrencyMode.Single? Или верно обратное?

Вероятно, это будет зависеть от услуги.

При InstanceContextMode.PerCall новый экземпляр службы создается для каждого вызова через прокси-сервер, поэтому вам приходится иметь дело с созданием дополнительных экземпляров. Предполагая, что ваш конструктор сервисов мало что делает, это не будет проблемой.

При InstanceContextMode.Single существует только один экземпляр службы на время существования приложения, поэтому с созданием экземпляра практически не связано никаких накладных расходов. Однако этот режим позволяет только одному экземпляру службы обрабатывать каждый вызов, который когда-либо будет сделан. Таким образом, если вы выполняете несколько вызовов одновременно, каждому вызову придется ждать завершения других вызовов, прежде чем он будет выполнен.

Для чего это стоит, вот как я это сделал. Используйте PerCall контекстный экземпляр с Multiple параллелизмом. Внутри вашего класса обслуживания WCF создайте статические элементы для управления внутренним хранилищем данных, а затем синхронизируйте доступ к этим статическим элементам при необходимости с помощью оператора lock, полей volatile и т. Д. Это позволяет масштабировать службу. прекрасно, сохраняя при этом безопасность потоков.

7 голосов
/ 30 марта 2010

Я полагаю, что ответ заключается в том, что есть несколько потоков (на стороне клиента), использующих один и тот же экземпляр прокси-сервера, что потенциально позволяет использовать несколько вызовов в одном экземпляре. Эта запись содержит более подробное объяснение.

2 голосов
/ 19 августа 2011

InstanceContextMode.PerCall и ConcurrencyMode.Single должны подойти, если вы не используете двусторонние обратные вызовы на сервере. В этом случае вам нужно будет использовать ConcurrencyMode.Reentrant, иначе обратный вызов не сможет получить доступ к заблокированному экземпляру службы и возникнет взаимоблокировка.

Поскольку он создается для каждого экземпляра службы вызовов, другие потоки или вызовы не могут получить к нему доступ. Как указано в статье, упомянутой в других ответах article , такая комбинация все еще может быть проблемой, если сеанс создан на уровне привязки И вы используете тот же прокси-объект службы.

Так что, если вы не используете один и тот же прокси-объект или не используете сессионную привязку и не используете двусторонние обратные вызовы для клиента (скорее всего, они должны быть OneWay в любом случае) InstanceContextMode.PerCall и ConcurrencyMode.Single должны быть хорошими.

1 голос
/ 01 февраля 2012

Я думаю, все зависит от требований. Если мы будем звонить на одну и ту же услугу столько раз, то лучше использовать InstanceContextMode - Single, а режим параллелизма - несколько.

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