ОК, думаю, я подозревал.
Службы WCF по умолчанию однопоточные.Все вызовы и обратные вызовы направляются в один поток (или, если быть более точным, SynchronizationContext).
Мое приложение представляет собой однопоточное приложение WPF, поэтому SynchronizationContext устанавливается в поток отправки.
Когда поступает обратный вызов, он пытается упорядочить вызов в потоке диспетчеризации, который, конечно, блокируется в исходном вызове службы.Я не уверен, что он точно блокируется, но, очевидно, существует некоторая глобальная блокировка, которую он пытается получить перед ожиданием потока диспетчеризации.
Когда поток диспетчеризации затем вызывает службу снова, он блокируется при этой глобальной блокировке.
Два способа обойти это:
1) Во-первых, создать прокси службы в другом потоке.Вместо этого все вызовы будут маршалироваться через этот поток, и не имеет значения, что поток отправки заблокирован.
2) Примените атрибут [CallbackBehavior (UseSynchronizationContext = false)] к клиентскому классу, который реализует обратный вызов.Это означает, что WCF будет игнорировать контекст синхронизации при входящем обратном вызове и будет обслуживать его в любом доступном потоке.
Я пошел с 2. Очевидно, это означает, что мне нужно маршалировать обратные вызовы, которые могут обновить GUI досам отправляю поток, но, к счастью, моя реализация обратного вызова в любом случае является небольшой оболочкой, поэтому я просто выполняю _dispatcher.BeginInvoke () в каждом методе обратного вызова, чтобы маршалировать ASYNCHRONOUSLY.Затем поток рассылки будет обслуживать, когда у него появится шанс, который я и хотел в первую очередь.