4

I have read the following articles on StackOverflow: ConcurrentBag - Add Multiple Items? and Concurrent Dictionary Correct Usage but the answers are still somehow not obvious to me.

I have this scenario: I have Leaderboard table in the database and I update it periodically. To optimze the server, I cache the result, so I use ConcurrentDictionary (because there are different types of leaderboards, such as All-time, 3-days, 7-days, etc...).

Here are my code at the updating leaderboard:

        var leaderboards = business.UpdateLeaderboard(LeaderboardUpdater.LeaderboardDaySpans, LeaderboardUpdater.LeaderboardCount);
        this.LastUpdateTime = now;

        // The LeaderboardCache is ConcurrentDictionary<int, LeaderboardResponseViewModel>
        this.LeaderboardCache.Clear();
        foreach (var leaderboard in leaderboards)
        {
            this.LeaderboardCache.TryAdd(leaderboard.DaySpan, new LeaderboardResponseViewModel(leaderboard));
        }

Assume the user may request Leaderboard information at any time. So I have some questions:

  • Should I use Concat instead of foreach to ensure all items are added at the same time?
  • Even if I use Concat, how can I ensure that the user won't request at the middle of the Clear and Concat method?
  • Should I apply an additional lock? If so, how can I ensure concurrent read, since multiple read at the same time is okay?

1 Answer 1

2

You are managing concurrency at the wrong level. Apparently, you want to treat the dictionary contents atomically but you are synchronizing (unsuccessfully) at the item level.

Use the following data structure:

volatile Lazy<...> myCache = new Lazy<Dictionary<...>>(ProduceCacheValues);

When you want to refresh the cache values create a new Lazy and overwrite myCache.

Alternatively, just use a lock. For low-frequency short-duration operations that's usually good enough.

To clarify, there is no way to make multiple items or operations in a ConcurrentDictionary atomic.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, I learnt 2 new things: volatile and Lazy. But I need to ask 2 questions: 1. should I still use ConcurrentDictionary instead of normal Dictionary as in your example? 2. The inline function ProduceCacheValues uses leaderboards variable, which is EntityFramework entity, and the Context might already be disposed the time it is lazy-loaded?
You are supposed to not mutate the contents of the lazy object. Then, there is no need for synchronization. I don't know whether entities of EF somehow become invalid when the context is disposed. I usually never put any EF stuff in globals. I always copy out the data into clean DTOs. Safer.
Thank you very much. I only know that Dictionary is not thread-safe but do not know if it is thread-safe when being read only. About the Entities, I will do as you advise, I copy them into DTOs first outside the Lazy factory method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.