Gian Lorenzo Meocci
Gian Lorenzo Meocci

Reputation: 1176

Is there a way to design this "static class" so that it is thread-safe?

I have a class with all methods static, like this:

class A { 
public:
   static std::string getA() { GlobalData::alfa; }
   static std::string sum(int x, int y) { ... }

   static int convert() { ... }

};

I need that A could be thread-safe. Whitch is the better design for do that? I need to convert all methods in a non-static method like this?

class B { 
public:
   std::string getA() { g.alfa; }
   std::string sum(int x, int y) { ... }
   int convert() { ... }

private:
   GlobalData g;
};

Consider that GlobalData is a simple POD like this:

struct GlobalData
{
   static std::string foo;
   static int bar;
   ...
}

Upvotes: 3

Views: 352

Answers (2)

didierc
didierc

Reputation: 14730

You can keep the original layout of class A, or even change it to a namespace instead, but you will have to define the GlobalData struct as thread local storage, if the data it contains must be specific to each thread:

 struct GlobalData {
    static thread_local std::string alfa;
    // other members here
};

You will probably need to call a function to initialize the data as needed for each thread.

Note that you could also turn that struct into a namespace if all the members have been defined static:

namespace GlobalData {
    thread_local std::string alfa;
    // etc.
}

namespace A {
   std::string getA() { return GlobalData::alfa; }
   std::string sum(int x, int y) { /* ... */ }

   int convert() { /* ... */ }
}

which improves your code readability.

The same rule should apply for any piece of data of global scope in your original code which must become thread specific.

Upvotes: 2

utnapistim
utnapistim

Reputation: 27365

A better design would be to not have A use a static implementation at all. This would mean you create an A instance and use it in client code through dependency injection.

Then, you can implement the RW access to A's data, using standard synchronization primitives.

Since your A has static state and A is not an injected dependency, the code using A internally would be synchronized on the synchronization primitives you want to add in A's implementation. This introduces potential deadlocks that are completely invisible from client code, that could be difficult to find and diagnose (depending on the complexity of the interactions of client code of A).

Upvotes: 1

Related Questions