开发者

WCF client deadlocking due to callback even when callback IsOneWay

开发者 https://www.devze.com 2023-03-11 16:01 出处:网络
new to WCF. I have a client which is deadlocking when calling a WCF service. The service will invoke a callback to the client at the time of the call which is marked as IsOneWay. I have confirmed t

new to WCF.

I have a client which is deadlocking when calling a WCF service.

The service will invoke a callback to the client at the time of the call which is marked as IsOneWay. I have confirmed that the service is not blocking on the callback.

The client then immediately calls the same service again (in a tight loop), without having yet serviced the callback. The client then deadlocks (and a breakpoint on the service side never gets triggered).

So to recap:

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)

I am assuming that the callback has grabbed some WCF lock at the client end, and then the second service call from the client also wants that lock, so deadlock results. But this is just assumption.

I have read about ConcurrencyMode but I ca开发者_开发问答n't decide which mode to use, or where to put it because I'm not 100% clear on what is going on, and what is being blocked exactly.

I would also prefer to keep all callbacks being serviced by the dispatch thread if possible as it keeps the code simpler.

Can any WCF experts shed light on exactly what is going on here?

Many thanks


OK, think I've sussed it.

WCF services default to single threaded. All calls and callbacks get marshalled to a single thread (or SynchronizationContext to be more accurate).

My app is a single threaded WPF app, so the SynchronizationContext gets set to the dispatch thread.

When the callback comes in it tries to marshal the call to the dispatch thread, which of course is sat blocking on the original service call. I'm not clear it locks exactly, but there's obviously some global lock that it tries to get before waiting for the dispatch thread.

When the dispatch thread then calls the service again, it deadlocks on this global lock.

Two ways around it:

1) Create the service proxy on a different thread in the first place. All calls will get marshalled through this thread instead and it won't matter that the dispatch thread is blocked.

2) Apply [CallbackBehavior(UseSynchronizationContext = false)] attribute to the client class that implements the callback. This means WCF will ignore the synchronisation context when the callback comes in, and it will service it on any available thread.

I went with 2. Obviously this means I need to marshal callbacks that could update the GUI to the dispatch thread myself, but luckily my callback implementation is a small wrapper anyway, so I just do a _dispatcher.BeginInvoke() in each callback method to marshal ASYNCHRONOUSLY. The dispatch thread will then service when it gets a chance which is what I wanted in the first place.


The sequence that you have depicted resembles a synchronous call. While in an async call, the sequence would be:

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...

In each case of each async web service call, the system creates a separate IO thread(IOCP thread), and processes the request. In this, seldom you will find a deadlock.

I have found this way, even when called within a loop, to be working very well.

You can, for instance, register for the event .OnProcessComplete, and then call the ProcessCompleteAsync method.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号