Подтвердить что ты не робот

Возможны ли проблемы concurrency при использовании атрибута службы WCF Service, установленного в ConcurrencyMode.Multiple и InstanceContextMode.PerCall?

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

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

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

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

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

Спасибо!

4b9b3361

Ответ 1

Единственный способ иметь два разных клиента 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 означает, что объекты обслуживания могут быть выполнены несколько потоков в любой момент времени. В в этом случае вы должны убедиться, что поток безопасность.

ИЗМЕНИТЬ

Вы спросили

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

Вероятно, это зависит от службы.

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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