4

For example, there are 8 threads use an uint64 as shared memory and use different bytes in that area (without lock).

One thread can only access to a certain byte so no byte contention exist.

In this case, is it safe? And from performance perspective, is it better to use CAS to operate the whole integer rather than each bytes?

WoooHaaaa
  • 19,732
  • 32
  • 90
  • 138
  • 3
    C++ handles thread safety on the order of objects, not "bytes in memory". If multiple threads are accessing the same object, then there's a data race, even if they're using masking to only access "different bytes". Please provide a [mcve] so that we can see whether what you're doing is safe. – Nicol Bolas Jul 16 '20 at 06:42
  • 4
    Please show a [mre], the answer is probably no it's not safe – Alan Birtles Jul 16 '20 at 06:42
  • The devil whispers in my ear: "[union](http://coliru.stacked-crooked.com/a/13deeeebfc33e6c0)". – user1810087 Jul 16 '20 at 08:21
  • 1
    The angle screems at me: "[**don't** use unions for type punning](https://stackoverflow.com/questions/25664848/unions-and-type-punning) – user1810087 Jul 16 '20 at 08:29
  • May I recommend a book "concurrency in action", it explains this in great detail. – rustyx Jul 16 '20 at 11:37

2 Answers2

4

No, it is in general not safe. Those bytes of a multi-byte variable are not written on their own. The whole variable is read, modified and written back.

So Thread A may read it, modify it, and then thread B reads it, thread A writes it, thread B modifies and writes it. The changes from thread A would be lost in this case.

You need to use separate variables or a synchronizing mechanism.

For the performance part: You have to test for it. Different code, hardware, data sizes and so on will give you different answers on what is optimal. As a rule of thumb, write simple and correct code first and optimize later, when it is really needed.

SKCoder
  • 409
  • 3
  • 10
1

I think it is safe. Please, correct me if I'm wrong. But keeping in mind what Mike Acton wrote in his article about strict aliasing rules:

It is always presumed that a char* may refer to an alias of any object. It is therefore quite safe, if perhaps a bit unoptimal (for architecture with wide loads and stores) to cast any pointer of any type to a char* type.

We can do the following:

    uint64_t shared_64 = 0x0102030405060708;
    char* thread_1_char = reinterpret_cast< char* >( &shared_64 );
    char* thread_2_char = reinterpret_cast< char* >( &shared_64 ) + 1;
    char* thread_3_char = reinterpret_cast< char* >( &shared_64 ) + 2;

    char& thread_1_ref = *thread_1_char;
    char& thread_2_ref = *thread_2_char;
    // and so on
    thread_1_ref = 1; // from one thread
    //...
    thread_2_ref = 2; // from another thread

Now each thread_n_ref variable is a distinct memory location and keeping in mind what topic starter said:

One thread can only access to a certain byte so no byte contention exist.

And from this

Different threads of execution are always allowed to access (read and modify) different memory locations concurrently, with no interference and no synchronization requirements.

I conclude that accessing different bytes of an int64 from a different threads is safe.

alex_noname
  • 26,459
  • 5
  • 69
  • 86