Блокировка клиента WCF из-за обратного вызова даже при обратном вызове IsOneWay - PullRequest
7 голосов
/ 08 июня 2011

новичок в WCF.

У меня есть клиент, который блокируется при вызове службы WCF.

Служба будет вызывать обратный вызов для клиента во время вызова, который помечен как IsOneWay. Я подтвердил, что служба не блокирует обратный вызов.

Затем клиент немедленно снова вызывает ту же услугу (в узком цикле), еще не обслужив обратный вызов. Затем клиент блокируется (и точка останова на стороне службы никогда не срабатывает).

Итак, резюмируем:

CLIENT                                SERVICE
Call service -----------------------> (service breakpoint triggers)
(waiting for dispatch thread) <------ Invoke callback (IsOneWay - doesn't block)
                                      Service returns

Call service again immediately -----? (service breakpoint doesn't trigger)
(deadlock)

Я предполагаю, что обратный вызов захватил некоторую блокировку WCF на стороне клиента, и затем второй вызов службы от клиента также хочет эту блокировку, поэтому возникает взаимоблокировка. Но это только предположение.

Я читал о ConcurrencyMode, но не могу решить, какой режим использовать или где его установить, потому что я не на 100% уверен в том, что происходит, и что именно блокируется.

Я бы также предпочел, чтобы все обратные вызовы обслуживались потоком диспетчеризации, если это возможно, поскольку он упрощает код.

Могут ли эксперты WCF пролить свет на то, что здесь происходит?

Большое спасибо

Ответы [ 2 ]

21 голосов
/ 09 июня 2011

ОК, думаю, я подозревал.

Службы WCF по умолчанию однопоточные.Все вызовы и обратные вызовы направляются в один поток (или, если быть более точным, SynchronizationContext).

Мое приложение представляет собой однопоточное приложение WPF, поэтому SynchronizationContext устанавливается в поток отправки.

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

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

Два способа обойти это:

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

2) Примените атрибут [CallbackBehavior (UseSynchronizationContext = false)] к клиентскому классу, который реализует обратный вызов.Это означает, что WCF будет игнорировать контекст синхронизации при входящем обратном вызове и будет обслуживать его в любом доступном потоке.

Я пошел с 2. Очевидно, это означает, что мне нужно маршалировать обратные вызовы, которые могут обновить GUI досам отправляю поток, но, к счастью, моя реализация обратного вызова в любом случае является небольшой оболочкой, поэтому я просто выполняю _dispatcher.BeginInvoke () в каждом методе обратного вызова, чтобы маршалировать ASYNCHRONOUSLY.Затем поток рассылки будет обслуживать, когда у него появится шанс, который я и хотел в первую очередь.

0 голосов
/ 08 июня 2011

Изображенная вами последовательность напоминает синхронный вызов. Во время асинхронного вызова последовательность будет такой:

Client                            Server
Call service    --------------->ProcessRequest(1) //Your for loop for instance.
Call service    --------------->ProcessRequest(2)
Call service    --------------->ProcessRequest(3)
Call service    --------------->ProcessRequest(4)
Call service    --------------->ProcessRequest(5)

Callback awake  <---------------Response1 //Responses tends to pour in...
Callback awake  <---------------Response2
Callback awake  <---------------Response3
Callback awake  <---------------Response4...

В каждом случае каждого вызова асинхронной веб-службы система создает отдельный поток ввода-вывода (поток IOCP) и обрабатывает запрос. В этом редко вы найдете тупик.

Я обнаружил, что этот способ, даже когда вызывается в цикле, работает очень хорошо.

Вы можете, например, зарегистрироваться для события .OnProcessComplete, а затем вызвать метод ProcessCompleteAsync.

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