C++ Multithreading – How to Facilitate Thread-Safe Access to Shared Variables

cmultithreadingperformanceqt

I have 2 sets–inputs and outputs–of 70 32-bit integer variables and 70 bools (140 vars altogether). These need to be accessed and modified from 3 threads. What is an appropriate design pattern to facilitate thread-safe read-write access to each of these 140 variables without locking all of them under a single mutex (which I expect will result in bad performance)?

Some details about the performance requirements:

  • Thread 1 ("CAN Serial Communication") receives packets from hardware sensors every 1ms that contain the updated value for one of the 70 input shared variables; the thread updates the variable with that value. Also, every 5ms Thread 1 needs to make a copy of all the 70 output variables.

  • Thread 2 ("Controller") creates a copy of all input variables every 10ms, as well as overwrites all the output variables.

  • Thread 3 ("GUI") makes a copy of all input and output variables every 500ms.

  • The system runs on an ARM Cortex-A8 600Mhz.

One solution is to create a mutex lock for each of the 140 variables, but this feels like a hack. I would then wrap the variables in a class with 140 getters and setters, which also seems ugly.


A side-note about std::atomic:

The other alternative is std::atomic. But I feel it is an advanced and complicated feature, for example I was told on IRC that the following example snippet is not thread-safe, despite looking intuitively like it should be:

typedef struct MyStruct {
        std::atomic<int> a;
        std::atomic<int> b;
}

std::atomic<MyStruct> atomic_struct;
atomic_struct.a = 1;
atomic_struct.b = 2;

// Make a copy of `atomic_struct`
Mystruct normal_struct;
normal_struct = atomic_struct;

// Edit the values of the copied struct and copy the changes back to the `atomic_struct`.
normal_struct.a = 100;
normal_struct.b = 200;
atomic_struct = normal_struct;

Best Answer

Comments turned into an answer:

You are right to worry about performance with locking everything under one mutex, but the better solution is to make sure there is as little going on as possible inside the lock.

Thread 1 should have the value and index ready and really only be doing a single write. Thread 2 would operate on an unshared local instance of the class, and swap it with the shared one. Thread 3 also has an unshared local instance that it copies the share into each update