Pergunta

I have a WCF service that is in turn calling several other web services. The services are ordered by preference but called in parallel. Each service will respond with true or false, and when all of the responses have been received, the most preferred positive will be returned to the client.

However, at any point during this, the most preferred service might respond with a positive. At this point it makes no sense to continue querying other services, so I wish to cancel the execution and return the result to the client.

That much I can do:

// Use parallelOptions to store the cancellation token
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
Parallel.ForEach<IntegrationObj>(externalServices, po, x =>
{
    try
    {
        // Send requests
        SendExternalRequest(x);
        po.CancellationToken.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException e)
    {
        /// Don't throw an exception if the loop was cancelled
    }
    catch (Exception e)
    {
        LogError(e.Message);
    }
});
if (positiveResponses.Count > 0) {
    // Get the most preferred positive response and send it back to the client
    res = positiveResponses.Where(
        x => x.ID == PreferredServiceID)
        .FirstOrDefault();
}
return res;

The above code is a very simplified version of my code, but essentially it checks each response to see if it is currently the most preferred service. If so, it cancels the loop.

However... Cancelling the loop in this way will wait for any services that have already been called to finish before it terminates the loop. There is a reasonable chance that any of the services could time out, which is of course treated as a negative. If just one of those times out, it adds an extra 10 seconds onto my response time.

What I would like to do is one of the following:

  1. Return the preferred service to the client and then continue processing the responses that come in from services that have already started (this is purely for logging purposes so that I know the positive/negative rate of those services).
  2. Cancel the loop instantly and forget about the remaining responses. Ideally I don't want to do this because it means services that would have responded positive will not be recorded as such, which will affect their preference rating.

So my question is this:

Is what I want to achieve possible? I realise that parallel.foreach might not be the best tool for the job, or that I might not be able to log responses after responding to the client. The most important requirement is that the client receives a fast response without having to wait for services to respond when the "winner" has already been picked.

Foi útil?

Solução

The way it's currently set up is that all your services are started parallel but your check is only programmed to happen after all of them are finished.
If you want to stick with your foreach approach you could write your if condition inside the foreach , that way if a service finishes first it will directly execute the check on positive responses and you can react accordingly(return the value / kill the others).
Note that this approach has more thread insafeties which you'll have to take care off(your reading from a list that one of your services might be trying to write to). It's also possible that 2 services finish at the same time so your if statement fires twice.

An alternative for the foreach approach would be to create a thread for each service, put all threads in a list and right after starting all of the threads start a while loop to keep checking(with a sleep off course) for a finished service. This way you can return the value while other threads are still running by breaking the while loop.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top