loading...

Is Encapsulation good for multithreading?

ajanibilby profile image Ajani James Bilby ・3 min read

This is designed as a question to the programming community as a whole. Thus I will refrain from any specific programming language.
This is a very important discussion to have due to the rise of heavily multi-core processors (cough cough Ryzen), and creating programs which can actually utilize all of those cores will become critical as the overhead for running multiple copies of a program instead of one parallelly scalable one seems to be a common approach to utilizing current multithreading capabilities of servers.

What is the best way to handle data which needs to be shared across multiple running threads without having access to collisions or bottlenecks due to accessing shared memory?

This will heavily depend on the workload, much like how one could argue linked lists are better than vectors.

While I can acknowledge the downfalls of object-oriented programming, and wish functional programming was used more so people can realise where it is best used, sadly I think one of the optimal solutions does from for OO.

Let's say you're shared object (counter) is a thread locked class (class methods can only be executed in the thread the object was originally initialized in). This means when another thread executes one of this object's methods it is added to the owning thread's task queue.

Now let's say that you have another two threads (neither of which own counter) which are doing some big number-crunching that generates numbers that need to be added to the counter. Each of these threads can keep generating these numbers and throwing off new tasks for the owner of counter, and as long as the function call is handled asynchronously the two threads can continue their number crunching.
These two threads are able to alter the shared memory without them waiting for any atomic operations (depending on how schedules are managed), instead, they are just generating some callback handles and continuing on their way which creates an ideal situation with no slowdowns.

Only when one of the two threads attempts to get the value of the counter (through a method) would it then have to wait. This can then also create an interesting scenario, if the owner of counter was busy with another task(s) and hadn't processed all of the add methods, the get would have to wait until they had been processed before it gets a response. This means that by the time it gets the returned value the add methods will have been correctly applied effectively making a kind of lazy calculation scenario. And also any new add methods that were called after the get was will then be processed after the get, meaning the value will not be peaking forwards in time for when it was called.
(Presuming that the scheduler never alters the order of the tasks).

Obviously this can be applied to much more complex shared objects making it more useful, however, this is an interesting way of removing a lot of data race uncertainties as well as atomic calls (though this also depends on how the threads interact with the scheduler). If a programmer completely encapsulates the shared object's attributes there will be no data race conditions no matter how poorly the object is used, thus it is an interesting way of wrapping safeties around shared data.

I presume there are alternative methods for solving such a problem, and there will also be people who are disgusted by my preposition. I would like to hear all of your comments so I can learn about different multi-threading approaches and hopefully spark an interesting discussion of what really needs to be had with the coming computer hardware.

Posted on by:

ajanibilby profile

Ajani James Bilby

@ajanibilby

Programming for over 5yrs, just experimenting and building everything from scratch so I know how it all works.

Discussion

pic
Editor guide
 

Concurrent programming is a rapidly-evolving field in applied computer science. Every language seems to do things differently and there are different avenues through which programs can be "parallelised" -- using raw threads, an Akka-like actor model, etc. This stands in contast to theoretical computer science, which has had a theory of concurrent computation for decades.

Pure functional programming, where objects have no state, avoids concurrency issues entirely when all you're interested in is fast calculation. What you're describing sounds like it would be well-suited for a functional approach, provided user interaction and message-passing aren't integral to your solution. If they are, maybe you could use an actor model.

Like you say, different situations require different solutions. This is an area I'm starting to learn more about and would be interested in what others have to add to the conversation.

 

Obviously avoiding shared memory via functional programming stops race conditions. However not every application can do that.

If you have a web server, every request could go to a different threads' schedule to quickly enable parallelism, however if you have any sessions that need to be accessed or updated as part of this request you will need to use shared memory.

I was trying to ask about methods for controlling shared memory, I guess I didn't word it very well.