Tuesday, July 2, 2013

Actually I got tired of this stereotype of all Russians equating to Vodka drinkers, Babushkas, Izbushkas, and Ivans... This is ridiculous but having said that after reading a book on concurrency and going through some examples I found myself thinking about explaining different types of concurrency designs on example of several Russians drinking a bottle of vodka... And the fact that I have Russian background gives me a right to think about it and share with you. No, I ain't no racist! :( At the same time you could think whatever is dear to you ... say French having a bottle of Champagne... or Germans having a threesome... Bad for me I am not a German. :)))...

In this analogy we will make following association:

A number of Russian guys to number of threads performing a task

A task is to consume vodka

A resources is a bottle of vodka

Not important in the context of our example but important for your general knowledge of Russian customs is that traditionally number of Russian Vodka drinkers has to be not less than three. More is usually not good either as not everyone gets enough of vodka :D... And you must know that three is a magic number: Holly Trinity, two eyes and one nose, and other numerology stuff... you know...

In case of computers though number of threads is estimated based on a number of available CPU * number of Cores and taken into consideration blocking coefficient of the threads. The general rule is increase number of threads more than number of CPU*Cores per CPU if application does a lot of I/O in threads e.g. reading files, making a blocking DB call or Web request. Or contrary allocate number of threads close to CPU*Cores per CPU if application is mostly doing a heavy computation. But that is completely separate topic and for the simplicity of our example we will consider number of threads 2 or more where concurrency actually is starting to exist...

Shared mutable state

So here we go... In this case three Russian guys will have no glasses and will have to drink vodka straight up from the bottle... Imagine this situation three poor Russian guys will have to queue up and take turns to drink. That is worst thing could happen... Imagine all the hygiene and other things that could get wrong... Yes, it is good from brotherhood perspective... And it is not romantic at all as French might say... Unless it is one guy and two girls drinking a bottle of champagne and I am up for that...

Lets review the problem from serious angle. You have one bottle of vodka and three guys taking turns to drink it... Only one can drink while others have to wait - that is concurrency problem. If this was a competition and the task was to finish the bottle of vodka as fast as possible this team would loose as there is just too much waiting for a one shared bottle... So as you see Shared mutable resource creates contention when many parties trying to modify it. On another hand if you let all threads access it without waiting it will lead to unpredictable side effects in your application. In case of our example if you let three guys drink without waiting they will likely have a fight... :D

So again this is very bad design and has to be avoided as much as possible. Try not to share mutable state!

Isolated mutable state

In this case situation is similar Russian guys have a bottle of vodka to drink but this time they have glasses... Whats improved in this situation? First of all it is more polite... Secondly from resource sharing perspective there is still one bottle but before it can be consumed every one's glass has to be filled. So the task of consumption is divided and distributed before actual drinking can take place... :) This reduces waiting and increases time performing the actual task - drinking... Drinking can be done in several iterations by a small shot cups as some drinkers can consume more or less alcohol... Same with threads some will do work faster and some slower depending on how heavy is the task, how much I/O involved and how load is distributed across threads... In programming this model is commonly implemented via the thread pools or executor service when you have one executor thread distributing the load and multiple worker threads performing the task. The actual task is allocated before execution and threads may comeback and given more work to perform.

So in this case there is no waiting during processing / consuming the alcohol but the only time drinkers have to wait is when the glasses are filled. The contention thus is minimized and everyone can enjoy their drink at the same time.

Immutable state

This is absolute "Nirvana" for our drinkers! In this case all of them will receive a separate (copy of the ) bottle of vodka (or bottle filled up to the portion that has to be consumed by a drinker so overall it adds up to the full bottle of vodka). Thus there is no dependency between drinkers at all. They can all drink at their own pace and do not have to coordinate drinking at all! In computer world this is best possible situation for threads when the shared mutable state removed completely and all threads have their own copy of the object to work with. In this case there is no contention at all and so no waiting, no risk of side effects in your application. Best possible scenario!

Having said that and returning to Russian drinking traditions last example is considered a pure form of alcoholism and not supported by Russians... Drinking is more of a social tradition when you have good friends sharing and making interesting conversations, discussing politics and other philosophical issues like purpose of life, women, fishing, etc...

3 comments:

Brilliant! Really helped me understand, but (not trying to insult your work), there are a couple of grammatical mistakes I could correct for you (free of charge, of course) just to make it seem a little more professional, and really convey the information as best as possible.Thank you for taking the time to read and/or reply,Simon Vercoe