If I have a service defined so:
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IMyService
{
[OperationContract(IsOneWay = true)]
[ReceiveContextEnabled(ManualControl = true)]
void DoSomething(Message<XElement> message);
}
and I want to call it asyncronously from my client (using shared contracts not generating from svcutil or add service reference) I can do:
Task task = Task.Factory.StartNew(() => myService.DoSomething(message));
... some other code
task.Wait();
I could also define my service to be async:
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface ICacheKeyExchangeAsync
{
[OperationContract(IsOneWay = true, AsyncPattern = true)]
[ReceiveContextEnabled(ManualControl = true)]
IAsyncResult BeginDoSomething(Message<XElement> message, AsyncCallback callback, object state);
void EndDoSomething(IAsyncResult result);
}
and do this instead
IAsyncResult result = myService.BeginDoSomething(message, null, null);
.... some other code
myService.EndDoSomething(result);
Are there significant differences between the approaches?
Yes, there are a differences in Thread Pool threads utilization.
CLR thread pool splitts threads on two types: worker and I/O (more information about them you can find in Simple description of worker and I/O threads in .NET and on MSDN). Generally speaking, thread pool gives you 250 worker threads per core and 1000 I/O threads, so you can use worker threads to process your WCF service input, and I/O threads to wait for asynchronous send/receive operation completion (which is supported on Windows OS level by overlapped I/O mechanism).
Keeping above in mind, let's have a look which threads are being utilized for both approaches by using ThreadPool.GetAvailableThreads() method:
I'll just show results of Thread Pool utilization for client side, but it's the same for server side as well.
APM approach for one way WCF operation.
For WCF contract:
Lets send 100 requests from client to server using next code:
Output is:
As you can see all worker threads are available on my x4 core machine and several I/O threads are being utilized.
Running synchronous one way operation as TPL Task.
For WCF contract:
Let's run 100 requests from client to server using next code (just want to notice that TPL uses CLR ThreadPool underhood):
Output is:
As you can see, now worker threads are being utilized, but not I/O threads.
So, what's the recommended approach?
As summary, your solution should:
So, recommended approach is Task-based asynchronous pattern for WCF, that satisfies all requirements above.
Task-based asynchronous pattern for WCF.
For contract:
Lets send 100 requests again:
Output:
OneWay = true
If you use the
OneWay
attribute, the client won't wait for the service to finish execution of the method. You can test that out easily by creating a service method which doesn't do anything but waits. A client will call the service method (even synchronously) and move on.You can test that very easily by writing a simple test method in your service:
and check the behaviour when you call it with and without the
OneWay
attribute. As such, it's somewhat pointless to call aOneWay
method it asynchronously, although I suspect doing so means you're pushing very minor things (like creating the request and sending whatever data you send) to another thread, so it might still be useful.AsyncPattern = true
This is useful if you want the client to wait for the operation to end (before it starts another for example). In case of
OneWay
, the client will send a request and forget about it - it doesn't care what's going on. With anAsyncPattern
the client will wait for a notification when the service finishes executing the method.The pattern also has one more added benefit - if you need it, it allows you to run some code when the method finishes execution on the service. It's useful when, for example, creating a
DuplexService
, which needs to manage client handlers and send notifications to clients when certain events occur.PS. I'm a bit uncertain with regards to this part of your post: "using shared contracts not generating from svcutil or add service reference". I don't think it matters for my answer, but just in case, I'm leaving this disclaimer here. ;)