locked
Asp.net core thread safe global variable RRS feed

  • Question

  • User-1309315418 posted

    I have a .net core razor app that has a special use case where I need to have a global counter that will be incremented by every request, and yet it should remain thread-safe.

    Also at web application load, this counter will be read from a database using entity framework.

    I don't think that static variables are threadsafe for this scenario (since it can be read by a request then be modified after another request read its previous value)

    So I was wondering if a locking mechanism is needed, or should a concurrent dictionary with a Lazy<T> be used ?

    Friday, April 24, 2020 2:01 PM

Answers

All replies

  • User475983607 posted

    Create a singleton middleware service.

    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1

    Friday, April 24, 2020 2:29 PM
  • User-474980206 posted

    You are correct you need to lock the variable for update. While a spin lock would work, if you just need to increment, use interlocked

    https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=netframework-4.8#System_Threading_Interlocked_Increment_System_Int64__

    initial the variable in startup, so there is no contention.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, April 24, 2020 2:29 PM
  • User-1309315418 posted

    could you elaborate more ? how would a middleware service contain a static variable that will be read/write globally and remain thread safe (so that a request doesnt increment while another reads the value before update)

    Friday, April 24, 2020 2:32 PM
  • User753101303 posted

    Hi,

    Or maybe you have a better option depending on the purpose of this counter?

    Friday, April 24, 2020 2:33 PM
  • User-1309315418 posted

    Hi,

    Or maybe you have a better option depending on the purpose of this counter?

    The counter is basically a seed for URL shortener service, every request must have a unique counter value that will be encoded into base62 and act as the shortened url

    Friday, April 24, 2020 2:35 PM
  • User-474980206 posted

    a couple of issues of using a static for this:

    1) you can only have a single server. if more than 1 you must give them ranges

    2) unless you persist the counter on each increment (than you might as use database locking), a recycle or crash will lose the count. To get around this you typically request a range. say you will do locally inc a hundred times before updating the persistent store. this means you update the saved counter by 100 (using locking). in the case of a crash, you just get a gap in numbers.

    Friday, April 24, 2020 3:20 PM
  • User753101303 posted

    Sounds like an "auto incremented column" which is found in most if not all db engines? It will be the id in a table of a row that gives the expanded url ?

    Friday, April 24, 2020 3:25 PM
  • User-1309315418 posted

    Sounds like an "auto incremented column" which is found in most if not all db engines? It will be the id in a table of a row that gives the expanded url ?

    I dont want to access the db on every request.  The idea is to have a local variable on server that is threadsafe so that no race condition will happen on increment/read from different requests

    Friday, April 24, 2020 3:27 PM
  • User-1309315418 posted

    a couple of issues of using a static for this:

    1) you can only have a single server. if more than 1 you must give them ranges

    2) unless you persist the counter on each increment (than you might as use database locking), a recycle or crash will lose the count. To get around this you typically request a range. say you will do locally inc a hundred times before updating the persistent store. this means you update the saved counter by 100 (using locking). in the case of a crash, you just get a gap in numbers.

    Couldn't agree more on what you said here, but on using interlocked 

      sPostValue = Interlocked.Increment(ref Startup.Counter)

    This should be fine, as it will increment first then read value and interlocked class here should handle thread-safety ?

    Friday, April 24, 2020 3:31 PM
  • User-474980206 posted

    yes. it two threads call interlocked at the same time, they both will get different values. its the simplest and fastest solution to your requirement.

    note: the interlocked library is designed around hardware instructions, so no actual software locking (like a spin lock) is required.   

    Friday, April 24, 2020 3:38 PM
  • User753101303 posted

    There is no race condition. Basically you are trying to what a db does already. As pointed also it will still work if you add load balanced servers and I have a hard time that inserting an url in db could be such a performance issue.

    Which db are you using? You tried already this feature or you are new to this ? Ultimately pick whatever best fit your needs but make sure to not try to solve a problem you don't have...

    Friday, April 24, 2020 3:40 PM