1

I'm new to C++ multithreading (apparently it's different from python multithreading / multiprocessing since multiple threads can use multiple CPUs inside a single process). I'm aware that a race condition would occur if 2 threads try to change the same data at the same time or if one threads reads something while another is changing it, but I'm not sure if the following situations would require synchronization:

let's say I have the following classes:

class Animal{
public:
    string name_;
    Animal(string name);
    ~Animal();
};

class Dog : public Animal{
public:
    int price_;
    Dog(string name, int price);
    ~Dog();
};

class Cat : public Animal{
public:
    int price_;
    Cat(string name, int price);
    ~Cat();
};

void do_stuff(){
    Animal* a = new Dog("Foo", 3);
}

Is it safe to:

  1. have one thread do a static_cast or a dynamic_cast while another thread is reading or writing to the object?
// thread 1
a->name = "Bar";
// thread 2
Dog* d = static_cast<Dog*>(a);
  1. have one thread read an attribute of an object while another thread is writing to another attribute of the same object? according the this post (Accessing different data members belonging to the same object from 2 different thread in C++) it seems to be okay but apparently it can cause problems with caching?

Thanks

qwerty_99
  • 640
  • 5
  • 20
  • (1) static_cast doesn't even access the object at all - just the pointer. (2) did you read the answer? – user253751 Aug 26 '20 at 16:24
  • thanks! and yeah I read the answers and I get that it can make things slower, but I don't exactly understand what they mean by data members alignment – qwerty_99 Aug 26 '20 at 16:31
  • Alignment - The CPU and memory have no concept of types. Integer is a language construct. The compiler reserves 4 bytes for int. If those 4 bytes straddle a cache line, it might require 2 CPU instructions to complete it (i.e. it's not atomic). However, compilers generally align types on 4-byte/8-byte boundaries. And mainstream CPUs load and store operations are atomic on word-sized word-aligned locations. Bitfields are when different variables have weird sizes and share the same bytes. (ex: A is 17 bits and B is 15 bits. Both stored in one 4-byte chunk). – Humphrey Winnebago Aug 26 '20 at 22:35
  • False Sharing - cache lines are 64 bytes today. Writing to different words of a cache line is atomic, but can result in cache contention between multiple CPUs. By definition, it's not a data race, but results in poor performance due to cache utilization. – Humphrey Winnebago Aug 26 '20 at 22:38

1 Answers1

1

Don't go there.

If there's data that's only ever written/read by one thread, it isn't shared data by definition and it doesn't need to be global.

If there is data that is written by one thread and eventually read by another, use a lock, std::atomic, or some other synchronizes-with relationship and call it a day.

static_cast, dynamic_cast, and dereferencing are reads.

  1. Shouldn't be a race.

  2. Shouldn't be a race as long as the alignment is ok and the members aren't bit-fields.

But the bottom line is that in modern C++ you have to use the synchronizes-with relationship or explicitly tell the compiler that some data is shared via std::atomic. In any multithreaded program, even in toy examples, there comes a point where one thread reads something another thread wrote. At that point, synchronization is required.

Humphrey Winnebago
  • 1,512
  • 8
  • 15