Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interlocked.Increment on dictionary value

I'm trying to use a Dictionary to record the current request count per API path on a web service, and to increase and decrease the current count I thought a good way would be using Interlocked.Increment() because it increases the count and reads the count at the same time.

However the following code gave an error saying ref argument is not classified as a variable, I'm guessing it's because dict[key] is not a variable?

var dict = new Dictionary<string, int>();
dict.Add("a", 1);
int i = Interlocked.Increment(ref dict["a"]);

I knew Interlocked.Increment() cannot be applied to properties but wouldn't have thought accessing dictionary via the key would have the same problems.

What's the best way to go about this?

Edit: Here're some more details about this.

I'm trying to write some code to throttle the number of API calls on each API path of the web service, so I have two dictionaries, the policy dictionary which specify how many concurrent callers are allowed on each API Path and a second counter Dictionary to record how many callers currently are active on each API Path.

Before the web service executes any incoming request, it will check the above two dictionaries to decide whether the request should continue or simply return a HTTP 429 (Too Many Requests) response straight away.

Here is an extract of the code, it firstly checks if there's a matching policy and then if so it then checks whether the max allowed requests are breached.

public override async Task Invoke(IOwinContext context)
{
    var url = context.Request.Path.ToString();
    var policy = _policies.FirstOrDefault(x => x.EndpointPath == url);

    if (policy != null)
    {
        try
        {
            if (Interlocked.Increment(ref _currentRequests[policy]) > policy.MaxConcurrentConnection)
            {
                context.Response.StatusCode = 429;
                var message = string.Format(
                    "Max API concurrent calls quota exceeded, please try again later. Maximum admitted: {0}",
                    policy.MaxConcurrentConnection);
                context.Response.Write(message);
                context.Response.ReasonPhrase = "Too Many Requests";
            }
            else
            {
                await Next.Invoke(context);
            }
        }
        finally
        {
            Interlocked.Decrement(ref _currentRequests[policy]);
        }
    }
    else
    {
        await Next.Invoke(context);
    }
}
like image 416
Godsent Avatar asked Nov 18 '15 09:11

Godsent


People also ask

What does interlocked increment do?

Increments a specified variable and stores the result, as an atomic operation.

Is interlocked increment thread-safe?

The Interlocked class provides a number of static methods that perform atomic operations. These can generally be regarded as thread-safe.

What is a concurrent dictionary?

adjective. occurring or existing simultaneously or side by side: concurrent attacks by land, sea, and air. acting in conjunction; cooperating: the concurrent efforts of several legislators to pass the new law. having equal authority or jurisdiction: two concurrent courts of law.

What is Interlocked in c#?

Add Method (System. Threading) Adds two integers and replaces the first integer with the sum, as an atomic operation.


1 Answers

Store a mutable heap object in the dictionary:

ConcurrentDictionary<..., StrongBox<int>> dict = ...;
Interlocked.Increment(ref dict[...].Value);

StrongBox.Value is a mutable field.

like image 115
usr Avatar answered Oct 27 '22 01:10

usr