I have an application written in C++ that was originally single threaded, but, due to the increasing complexity of this project, I’ll need to expand it to at least two or three threads.
There are only a total of four variables that will be used between more than one thread threads, making it useless to make everything thread-safe. The obvious solution is to just create a mutex for each variable and move on with my life.
However, looking back, this seems like a horrible idea! I’m afraid that another programmer may forget that the majority of the methods and variables are not thread-safe and use a variable in another thread.
Since I’m the main programmer for this project, it doesn’t seem like this would be an issue, but I honestly feel like this is bad programming and it feels really messy.
What are some ways to keep the variables separate to make it easier to tell which variable is for which thread? I’ve been thinking about isolating the code for the thread so it cannot access the other global variables (giving a compile error instead of silently creating a bug). I could also create a namespace or class for each thread. Is there a best practice for this?
10
There are two elements to this problem: #1, the technical change to the existing code base to support multi-threading, and #2 changing the culture of the company in order to be aware of thread safety in the code.
The first is relatively simple. First, document the functions in the class that are thread safe and are not thread safe. I agree with Jeffery Thomas that you should treat it as a major new feature. Often, I’ll write an entirely new class with some (relation to the original class to keep it DRY) that is thread safe. While there is no explicit “thread safe” tag in c++, I find making the class const correct can do a lot to help out. You should also clearly document what is thread safe or not (follow a convention, be CONSISTENT, and make the assumed state not thread safe).
The larger problem is changing the culture of the company to make sure everyone is aware of thread safety. I find this is where things like a style guide and code reviews can be very helpful. A style guide can inform people exactly how to mark things as thread safe. We do a lot of C programming, so I’m partial to decorating names, but you should choose a style that works for you. Some companies lean towards making everything thread safe by default, but I find that tends to add a lot of unnecessary code complexity, and can add non-insignificant overhead on occasion.
Code reviews, when done correctly, are a way to educate everyone on the style and to pass around information about programming in general.
Wrap all access to the variables in a thread safe function so that access of the variables from anywhere in the code flows through a single method, and make the variable private.
This way any access of the variable from outside the class will be thread safe.
The next step is to move the variables into their own class, only accessible via the safe methods. (Not one class per variable, just all the variable in a separate class.)
There’s always a way for another programmer to mess up the code, but in my experience if you write the code so that the easiest thing for them to do is the safe way, that will be what they do.
3
Adding multi-threading is a major new feature of a class. I think you should treat it like a major new feature, and don’t try to sneak it in.
Rethink and refactor the class and it relationships. I would look into creating a new class to represent each threaded task and/or slice of functionality.
If needed you can preserve the old class interface as a proxy to the new cluster of classes.
After some work, I actually figured out a simple solution for this issue (as simple as they get)… namespaces.
I have each logical thread organized into a specific namespace. For example, this is what my namespace organization looks like (split into different files, obviously). If I had a ton more stuff shared, I would’ve done a class approach like SumGuy’s answer for shared variables, but it’d be overkill to do this for all of the code since, besides the four variables, the threads are independent.
namespace MainThread {
}
namespace ThreadOne {
}
It isn’t a pretty solution, but at least another person will have to explicitly type the word “thread” to use a variable/function in another thread, so it’s their fault if they ignore such an obvious thing.
Also, if editing code in one namespace, it takes no extra effort to type the name of the current thread since that’s automatically assumed!