How to handle a multi thread tests calling a single thread web service in .Net Framework 4.6?

33 views Asked by At

The authentication PIN generation web service must send a notification or push to the requester for each generated PIN.

However, when a multithreaded call to this web service occurs, the service fails to manage the threads and sends multiple notifications with the same PIN code. This is incorrect. The system generates distinct PIN codes, but it falters in sending the notifications.

In the case of a single-threaded call, this error does not happen, even with thousands of queued calls, it works fine.

Interface:

[ServiceContract]
public interface IGenPin
{
    [OperationContract]
    ResponseResult GenPin(ClientPin client);
}

Class:

public class GenPinWS : IGenPin
{
    public ResponseResult GenPin(ClientPin client)
    {
        ResponseResult msgResponseResult = null;
        string strGuidTrace = Guid.NewGuid().ToString().Replace("-", "");
        
        try
        {
            Traceability.InsertTraceability(...);

            ServiceGenPin _serviceGenPin = new ServiceGenPin();
            msgResponseResult = _serviceGenPin.GenPin(client, strGuidTrace); //here I send the PIN notifications

            Traceability.InsertTraceability(...);

            return msgGpinResponseResult;
        }
        catch (Exception ex)
        {
            ...
        
        }
        return msgResponseResult;
    }
}

I've already tried implementing semaphore and queue, but it didn't work. The code is synchronous and tightly coupled.

I expect to send the notification only once for each generated PIN code. How can I control these multithreaded calls from within my single-threaded web service?

2

There are 2 answers

1
Damien On

One simple solution would be to create a critical section like that:

public class GenPinWS : IGenPin
{

    private static readonly object access = new object();
    public ResponseResult GenPin(ClientPin client)
    {

        lock(access)
        {
            // Only one thread will be allowed to enter the **lock** block
            ResponseResult msgResponseResult = null;
            string strGuidTrace = Guid.NewGuid().ToString().Replace("-", "");
        
            try
            {
                Traceability.InsertTraceability(...);

                ServiceGenPin _serviceGenPin = new ServiceGenPin();
                msgResponseResult = _serviceGenPin.GenPin(client, strGuidTrace); //here I send the PIN notifications

                Traceability.InsertTraceability(...);

                return msgGpinResponseResult;
            }
            catch (Exception ex)
            {
                // ...            
            }
            return msgResponseResult;
        }
    }
}
0
Gerry Schmitz On

For situations like this, I use a ConcurrentQueue (static or otherwise). The entries have enough information for a BackgroundWorker (BGW) to dispatch them. When an entry is added to the queue, that "handler" checks to see if the BGW is running; if not it starts it up. When done with the current contents, the BGW exits until restarted. It's easy enough to test by adding entries to the queue at the "expected" rate and letting the "worker" process them; using the BGW's "progress reporting" and "completed" events to provide feedback. (I assume you can at least create a BGW; if not, you can still process the queue at a regular interval ... queue times are just longer without the BGW)