Last week I was working with one of my assignments where I need to deal with Golang concurrency. I was working with some in-memory data which gets modified by concurrent user request. I need to take control over reading & write operations on this in-memory data.

Here I am explaining how I dealt with it using RWMutex. I have taken an example of the implementation of an in-memory dictionary data structure to clear the concept.

Note: I expect you all have a beginner level understanding of golang syntax and primitive types to understand the source-code.

A dictionary is associated with a set of keys and each key has a single associated value. When a key is provided, the dictionary will simply return the associated value. Go provides a very convenient implementation of a dictionary by its built-in map type.

None
Dictionary structure

I have designed the following structure to represent dictionary objects across all snippets in this article.

It holds a map named items to hold all key-value pair data. The purpose of defining IKey and IValue types is to make it work generically. lock of type sync.RWMutex is associated with struct to control read-write operations on the dictionary. RWMutex exposes two methods used by readers (RLock and RUnlock) and two dedicated writers (Lock and Unlock).

Lock(): only one goroutine read/write at a time by acquiring the lock.

RLock(): multiple goroutines can read(not write) at a time by acquiring the lock.

The following are the operations that illustrate the uses of these methods while implementing operations on the dictionary data structure.

Add a key-value pair to the dictionary.

The dictionary object acquires its lock with dict.lock.Lock() before inserting a new item in items. It avoids dirty read operations. The lock is released after successful insertion in items. The lock release is achieved with defer dict.lock.Unlock().

If one request already acquired Lock(), then another request willing to get RLock() will have to wait until first one release lock (Unlock())

Remove key-value pair from the dictionary

The dictionary object acquires its lock with dict.lock.Lock() before removing an entry from the dictionary. It avoids dirty read operations. The lock is released after the successful deletion of the item specified by the key. The lock release is achieved with defer dict.lock.Unlock().

Removing an item from the dictionary is considered as write operations and hence implemented with Lock () & Unlock ().

Get an item from the dictionary by key.

It is important to point to emphasize that data races aren't a concern if the only thing you are doing concurrently is reading the shared data. To acquire a Lock() for writing it has to wait until RUnlock().

Check if a key present in the dictionary.

Checking if the item is present in the dictionary is considered as reading operations and hence implemented with RLock() & RUnlock().

Remove all elements from the dictionary.

Removing all items from the dictionary is considered as write operations and hence implemented with Lock() & Unlock()

Get the size of the dictionary.

Getting the length of the dictionary is considered as reading operations and hence implemented with RLock() & RUnlock().

Get a slice of all the keys present in the dictionary.

Retrieving slice of all the keys present in the dictionary is considered as reading operations and hence implemented with RLock() & RUnlock().

Get a slice of all the values present in the dictionary.

Retrieving slice of all the values present in the dictionary is considered as reading operations and hence implemented with RLock() & RUnlock().

Click here to review & run the above code on the golang playground.

Conclusion

  • A dictionary is associated with a set of keys and each key has a single associated value. When a key is provided, the dictionary will simply return the associated value.
  • Go provides a very convenient implementation of a dictionary by its built-in map type. Maps are not thread-safe by default.
  • Dictionary data structure can be easily implemented using maps and synchronized using RWMutex.

This is all for now…. Thank You for reading…..

@ Happy Coding…… :)