Home > OS >  I need to mimic a ConcurrentDictionary with a normal Dictionary in C#
I need to mimic a ConcurrentDictionary with a normal Dictionary in C#

Time:11-04

Just as I said in the title I need to mimic a ConcurrentDictionary using Dictionaries because I need to serialize the said collection and the concurrent variant is not serializable. Any Idea in how ConcurrentDictionary handles multi-threading and how I should implement it?

I haven't tried it yet, i feel like a fish out of water.

CodePudding user response:

BinaryFormatter serialization is considered obsolete. Try using Json serialization instead:

ConcurrentDictionary<string, Variant> dict = new ConcurrentDictionary<string, Variant>();
dict.TryAdd("one", new Variant() { Name = "one", Coordinate = new System.Drawing.Point() { } });
dict.TryAdd("two", new Variant() { Name = "two", Coordinate = new System.Drawing.Point() { } });    
var serialized = JsonConvert.SerializeObject(dict);

//This works just fine
var deserialized = JsonConvert.DeserializeObject<ConcurrentDictionary<string, Variant>>(serialized);

You can then save this string in the storage of your choice. Your data structures are not known so I just made something up for illustration purposes.

The one thing you will have to worry about is locking the serialization methods as you are attempting to do this several times per second. If you are saving it to disk, you are likely to run into file lock issues and what not. For this reason, you should reconsider your state saving strategy as well.

Something along the lines of:

private ConcurrentDictionary<string, Variant> _history = new ConcurrentDictionary<string, Variant>();

//Somewhere in your code you populate the dictionary
_history.TryAdd("one", new Variant() { Name = "one", Coordinate = new System.Drawing.Point() { } });
_history.TryAdd("two", new Variant() { Name = "two", Coordinate = new System.Drawing.Point() { } });

private object _lock = new object();
private void SaveHistory()
{
    lock (_lock)
    {
        var serialized = JsonConvert.SerializeObject(_history);
        DataLayer.SaveHistory(serialized);
    }                       
}

private void RestoreHistory()
{
    lock (_lock)
    {
        var serialized = DataLayer.GetHistory();
        _history = JsonConvert.DeserializeObject<ConcurrentDictionary<string, Variant>>(serialized);
    }
}

CodePudding user response:

Since the comments specified that this is for saving replays.

A better approach is probably to let the main thread, or whatever thread owns the data to be saved, create a copy of the data and put this in a buffer. This buffer can then either be kept in memory, or written out to disk concurrently with the game.

This way you avoid any risk of blocking the main game loop, and you are guaranteed to get consistent data. You could also add functions such as saving data ever n:th frame, or skipping data saving if the buffer is full.

The way I would implement this is with a concurrentQueue. Create a long running task that loops over myQueue.GetConsumingEnumerable(), and make sure to call CompleteAdding once done. But something like DataFlow might also be an option.

You should most likely also avoid BinaryFormatter, If speed is a concern I would probably use Protobuf .net with the SerializeWithLengthPrefix methods, that way you can write out multiple independent objects to the same stream.

CodePudding user response:

You can use lock statements to protect access to the Dictionary and ensure that only one thread at a time will do so. You can create an object to lock on, e.g.

private object syncRoot = new object();

and then lock on that:

lock (syncRoot)
{
    // Only one thread will be able to execute here at a time.
}
  • Related