It’s easy to assume that service operation contracts marked as one-way are asynchronous but that’s not the case (there’s a phrase about assumptions which springs to mind).
void KeepAlive(string userId, string sessionId);
A good example of when you might use a one-way operation is where your client application pings a service with keep-alive requests (say every 30 seconds) to keep a login session alive or maybe to maintain cached server state. And because of the apparent asynchronous nature of one-way operations you might be tempted to make these keep-alive calls directly from your primary (UI) thread.
However the reality is this: if calls are not dispatched immediately on the service due to a high volume of service requests, they are added to a queue (similar to how IIS handles HTTP requests under load). This is fine. But if the service is under particularly heavy load and the queue is already full, then the client thread which initiated the call will block until (a) the call can be added to the queue or (b) it times-out (the WCF default time-out is 60 seconds). So do not assume that making one-way service calls on the UI thread means that your client application will remain responsive.
One possible solution for implementing keep-alive requests (known as a heartbeat) for your client application is to make the WCF one-way service calls on a separate worker thread, leaving the UI thread free to continue handling user events.