#begin

Today we are going to continue with another chapter of the Clean Code book by Uncle Bob; Concurrency. I can remember in the very first blog that I said I was not sure if I was going to spend time on this chapter but I think I’ll do it anyway. For multiple reasons, first, this is an old book and it’s about Java. Multi-threading has become a very important concept “recently” because moore’s law stagnated. It’s the law that says that every 18 months the CPU power doubles. It as been that way for a long time in the past, but for about 10 years now, that’s no longer the case. So because of that, only in recent years is there much attention being given to mutli-threading because it’s a way to increase the throughput of your system. In the past, to make your system faster, you just waited for 18 months, and you got a free performance upgrade. But you can no longer do that. So what I’m trying to say is; because multi-threading has become such an important topic only over the past couple of years, the abstractions and utilities to use for it have greatly improved only over the past couple of years. So in C# we have this awesome Async/Await logic. That is not in Java, and certainly not in the book.

The second reason is that these blogs about clean code are mostly aimed at beginning unity devs. And as a beginner, you are probably not dabbling with multi-threaded code. So the timing feels a bit off if you ask me.

And reason number three is that; mutli-threading in Unity3D can be very difficult since the Unity3D api can only be accessed from the main thread and is thus not thread safe. So I can’t remember if many of the subjects that are in this chapter, are not going to apply in a Unity3D context, but we will find out soon enough. However, with the help of C#’s awesome async/await logic we can really simplify threading in Unity3D since it does offer support for that.

So despite this reasons, I’m still going to cover this chapter of the book since it is the only chapter left that I can meaningfully cover in a blog. The book has some more chapters where Uncle bob shows you have to refactor code to fit a clean code style. These chapters have lot’s and lot’s of source code and thus I cannot cover them in a blog without confusing everyone. But this chapter about concurrency is one with some practices etc. So if I cover this one, I have covered all chapters of the book that I can meaningfully cover and thus the series is complete. So let’s dive into chapter 13 Concurrency.

 

Chapter 13: Concurrency

On the first page of this chapter there is a really nice quote by James O. Coplien which says and I quote; “Objects are abstractions of processing. Threads are abstractions of schedule.” Isn’t that a nice observation? He’s totally right, right? I mean through threads we schedule different operations that are executed by objects. In an OO language that is of course. Yeah, pretty neat quote ;).

Uncle bob starts this chapter of with a nice observation of his own, and I totally agree. He says that and I quote: “Writing clean concurrent programs is hard – very hard. It’s much easier to write code that executes in a single thread. It is also easy to write multithreaded code that looks fine on the surface but is broken on a deeper level. Such code works fine until your system is placed under stress.”. I mean, does that sound familiar to you. It certainly does to me. But luckily we have got nice language support for threads in C# now which makes things for more easy than the classic threading abstractions. Which you can still use by the way. He also says there is a concurrency tutorial in the book which you can check out as well since the topic of Clean Concurrency is for a book of its own. I’m pretty sure that book has not yet been written but maybe we will see it somewhere in the future. Although I highly doubt it since Uncle Bob writes a lot of Clojure now instead of Java. And, in clojure, the multi threading model is far different and far more easy. But let’s continue with the book.

He then asks the question; Why do we use concurrency? He says it’s a decoupling strategy for separating WHAT gets done from WHEN it gets done. In a single threaded program, the what and the when are highly coupled. It is very “easy” for example to debug single threaded code, you can set break points and they will be caught in order. With multi threaded code, these breakpoints mght not be caught in order. Depending on where you put them of course. He also says that this decoupling through threading can really improve the throughput of your system. So decoupling through multi threading will turn your program that runs in one giant loop, into separate programs all running in different loops.

Unity3D does this as well right? I mean, imagine if your game logic would be on the same thread as physics, rendering and UI. Would that perform you think? I guarantee it would not. So multi-threading can be a very big performance boost but it will also bring new degrees of complexity.

Next Uncle Bob wants to bust some myths about concurrency and he listed seven of them. Let’s check these out for a minute. He starts of by repeating; multi-threading is hard. And you should consider these misconceptions about concurrency.

1: Concurrency always improves performance. Haha yeah that’s a common myth I think, Concurrency can sometimes increase performance but not always. It really depends on how much interactions, and waiting time there is between those threads. So, imagine a naive concurrency model where you would, let’s say, write out a file on another thread. If your main thread would have to wait until the file was written out before it can continue you will create a large lag-spike. This might be a bad example but I mean, you can write a massively concurrent program, but if your scheduling and coordination is not right, you might not see that much of a performance increase. But let’s look at myth two.

2: Design does not change when writing concurrent programs. This I just flat out wrong. Maybe, in c# when we use a lot of async/await we do not have to change our design that much but still there will be change compared to writing the same thing single threaded. Usually, decoupling the what from the when will change the way your game is designed. You can also see this effect with coroutines right? I mean when you use coroutines your system design is different than for example putting that logic in the update loop. And no, I’m not advising to abuse the update loop for all kinds of logic that could be executed elsewhere.

3: Understanding concurrency issues is not important working with a container such as a Web or EJB container. Haha, I agree. It’s always important to know what you are working with to some degree. There should not be any magic involved in your OWN system. I get that you will depend on other software that might seem like magic to you, but everything you build your own, should not be unknown to you. No matter if it’s about threading or any other kinds of logic like for example Dependency injection or maybe database mappings.

4: Concurrency incurs some overhead; both in performance and overhead. Hmm that’s interesting right. It incurs performance as well as overhead. He’s right, right? You will have some overhead with managing all these threads but you will also get some performance if done right.

5: Correct concurrency is complex; even for simple problems. Again, he’s perfectly right. I mean even for a simple multi-threaded piece of code that reads and writes files to disk. You need to manage what thread has the file currently opened and is writing to it. If you don’t, you will get a FileSharingViolation exception. So, it always requires you to think about these kinds of issues.

6: Concurrency bugs aren’t usually repeatable, so they are often ignored as one-offs instead of the true defects they are. Hmmm, this is a nice one isn’t it. I’ve ran into this myself. Concurrent code can be really difficult to test and debug so reproducing some weird bug or exception can be really, really difficult. Don’t underestimate this! You also need to consider the fact that, if you are using async/await, exceptions might not bubble up to the main thread once they are thrown on a sub-thread. So, if an exception is thrown in an async task, the exception might not be shown on the main thread and thus you don’t even see it. So make sure you cover tasks with proper try-catch statements for AggregateExceptions and make sure you catch them because you will find yourself debuging this.

7: Concurrency often requires a fundamental change in design strategy. I think we discussed this shortly already. Separating the what from when will most likely change the design of the code and the entire system. Async/Await tries to minimize this amount of change, but still, you will have to change your design based on this. It’s just a different mindset when things are happening async and even in parallel.

To mitigate some of the difficulties and problems Uncle Bob wrote down some concurrency defense principles. And he names the single responsibility principle once again. He says to keep your concurrent code separated from your single threaded code. Don’t start mixing them up. He says that concurrency related code has it’s own life-cycle of development, change and tuning. It also has it’s own challenges which are different and often more difficult than non concurrent code.

He then also makes the case that you should limit the scope of the data. If multiple threads are reading and or writing to the same data it can lead to many issues in OO programs. Rich Hickey, creator of clojure has a nice name for this and he calls it Place oriented programming because in OO programming languages we mostly have mutable data-structures which point to specific places in memory. Uncle bob’s advise is to protect these places by using special language features. Like locks, mutexes or semaphores and among others. He also says that making things effectively concurrent may lead to some duplicated code, which violates the DRY principle but in this case it is acceptable. I think we discussed this before somewhere, not all duplicated code is an actual duplicate in the sense that it does the same thing and has the same responsibilities. His recommendation is thus to use data encapsulation as much as you can to severely limit the access of any data that may be shared.

His next piece of advise goes a bit further on the place oriented programming and he says that in order to write good concurrent code, you must avoid sharing data as much as you can but use copies of data as a means of data sharing. So using immutable, unchangeable data structures in your concurrent code. A simple change you might apply is to use structs instead of classes although structs can still hold references to mutable data structures. So when you use immutable objects in your concurrent code you can avoid data synchronization altogether. It will make your code much more simpler and will make it easier to reason about what your code is doing.

Next Uncle Bob says you should have some knowledge of the libraries you are using that facilitate concurrent constructs like collections. DotNet has great support for concurrent collection types like concurrent queues, stacks, dictionaries and bag. I’ve used them all before and they work really great. If you have not, I strongly encourage you to do so. Using these concurrent collection types in combination with C# language support for async/await will allow you to create some reasonably elegant concurrent code.

He also says you should check some common or popular mechanisms like producer consumer patterns based on queue’s to decouple systems from each other. If you do not know about this pattern, it can be a life-saver in your concurrent code. So the producer consumer pattern separates concurrent entities from each other based on a simple concurrent queue. So, producers will enqueue data into the queue, and the consumers will dequeue data from it. This way, your producers decoupled from consumers making them scaleable and independent.

Next he says that you should also separate writing from reading logic. This is great advise! You can also create specific kinds of locking mechanism based on reading and writing which will also help you out a great deal. The thing is that, reading often does not block a thread, but writing does. So if you separate these concepts, you can greatly increase the throughput of your system. Since you can have multiple reads going on, yet one write action. So make sure to separate this! It will also simplify your code since, you can apply the SRP more effectively. See writing and reading as separate responsibilities and don’t violate the SRP by combining them.

Next is a more subtle piece of advise and that is you should be aware of dependencies in code you create by locking threads. When you lock a thread, on a specific piece of data, you other code will be dependent on that lock. So when you lock something, you can introduce things like dead locks or even livelocks. A dead lock is when 2 or more threads are waiting for each other to finish their task, but both have locked a resource and thus both cannot continue. This makes you code freeze in place, or well lock in place. A live lock is when threads are able to continue, but very slowly because they continually run into locked code which slows them down immensely. These are things to consider so, if you are going to do multi-threading, make sure you read up on the latest materials and algorithms because it’s probably harder than you think.

Next there is a section about testing concurrent code. This can be a real bitch in Unity3D since the unity test tools do not support async test cases. So what you will have to do is write some converters for tasks to coroutines, because Unity3D does support these [Unity-Test] annotated Ienumerator test functions. Writing an extension function for this is pretty easy, just yield the coroutine as long as the task does not have an exception or has not run til completion. These are properties you can really easily access for any given task. So to cheat the Unitytest to run async, convert your async logic into an Ienumerator and run it that way.

Uncle Bob gives a couple of nice recommendations for concurrent tests. First; Write tests that have the potential to expose problems and then run them frequently, with different programmatic configurations and system configurations and load. If tests ever fail, track down the failure. Don’t ignore a failure just because tests pass on a subsequent run. This is great advice, but also pretty hard to follow up on. I mean, if sometimes your tests fail, they are probably hard to reproduce and thus track down. But he basically says to not ignore system failures as one-offs. If you can embed such a one-off in a test, and you see it failing every once in a while, it’s no longer a one-off and thus you can try to figure out what’s wrong and fix the bug.

His next recommendation is to always get your nonthreaded code working first. Don’t try to solve both single and multi-threaded bugs in the same sitting or programming session. Make sure the single threaded code works fine in and of itself and then focus on the multi-threaded part. This will reduce the problem domain and space. He also specifically says to create lot’s of POCO’s. Remember them? Plain old C# Objects? These are object that have no dependencies on any external libraries or frameworks other than your own namespaces. He says that these POCO’s should be unaware they are accessed from multiple threads. And that might sound weird since you might sometimes need to add locking mechanism in place and thus add these directly in your POCO. But this is pretty rare I guess because you will most likely create special objects for multi-threaded code. That’s a good practice at least I think.

He then says to always try to make your concurrent code tunable. So for example, allow you code to accept a variable number of producers and consumers. This will make your performance scaleable out of the box, and it allows you to test under different configurations. He also says that you should run with more threads than there are processors. This will allow you to check whether task swapping on the same core is implemented correctly and thus you can find out about deadlocks quicker. Wow, this is some great advise. I’ve never thought about it this way even after reading this book for about 5 times now. Weird that only now I notice how good of an advise this is. Amazing.

Next he says you should run on multiple platforms for example; Windows vs OSx vs Android vs iOS. This is something you will have to do when you deploy anyway, so why not run the tests on multiple platforms. For Windows and OSx this is easy since you can run the tests in the editor on your local machines, or maybe in a docker container you hook up to your CI pipeline. Running your tests on mobile platforms is more tricky. And to be honest with you, not something I have ever done before. I’m not even sure you can run your unit tests directly on a mobile devise. If someone knows, please let me know and I’ll check it out.

The next thing Uncle Bob describes is to build in mechanisms to influence the threading logic. A simple example would be to force some object to have a higher priority to be processed by your concurrent code. This way you can play around with these exposed function and try to break your concurrent code. So you can try to force failures into your system by tweaking the settings during the tests run. Just make sure you do not use them in your production code unless you need to of course.

So to sum this chapter up; do not underestimate concurrent code. It’s often more difficult than you first think. But once you get it working properly you might get a really significant performance boost. I mean look at the massive performance boos the ECS gives. A topic we haven’t even really touched upon in this blog. Shit, I only mention it now. The ECS is a very promising project for Unity3D multithreading. But nonetheless, the ECS is worthy of it’s own blog since its a big topic and has a really large impact on how you design and create your game architecture. So we will get back to the topic of the ECS in a future blog I promise!

But OK, that’s a wrap for chapter 13, Concurrency. I’ve had a lot of fun talking about this since I really like the concept of concurrency. I wrote my master’s thesis about concurrency and scheduling , which I’m not getting into now since it’s not related to Unity3D or C#.

But this is the last chapter of the book that provides the reader, and listeners in this case with some advise. The remaining chapters are all case-studies of how Uncle Bob employs the strategies of clean code to existing code. He shows you how to refactor code to be clean with lot;s of code examples and all the steps that are involved. It’s really nice to read through this, but not so much to make blogs about. As I said at the beginning of this chapter. It’s really difficult to talk about the refactorings he makes and expect people to understand what I’m trying to say just by audio. So if you are interested in checking this out, I strongly encourage you to read the book.

I also see we have been going for far to long again so let’s wrap this blog up. The next blogs are going to be about alternatives about clean code and making some comparisons to clean code. As I said in the first blog in this series, I’m not saying clean code is the best, the only way to go. I’ve also read and learned a lot from books like the original Pragmatic programmer, Code complete and A Philosophy of Software Design. I’ve read them all but I think the Clean code book is the easiest one to get started with since it has great advise for juniors, lot’s of code examples and even these case-study chapters I’m not going to cover in this blog.

So I hope you schedule some async reading sessions in the future to follow up on the blogs I write on the alternatives to clean code, starting with: A Philosophy of Software Design!

#end

01010010 01110101 01100010 01100101 01101110.

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!