3

My code does some processing, and it writes intermediate results into an array, which I can later write to file. I want to break it into threads, but their processing regions will overlap, so some intermediate results will be written twice. Both threads will write the same value to the same array element.

Example:

#include <iostream>
#include <thread>

int array[4];

void f1()
{
    array[0] = 10;
    array[1] = 11;
}

void f2()
{
    array[1] = 11;
    array[2] = 12;
}

void f3()
{
    array[2] = 12;
    array[3] = 13;
}

int main()
{
    std::thread t1(f1);
    std::thread t2(f2);
    std::thread t3(f3);
    t1.join();
    t2.join();
    t3.join();
    for (int x: array)
        std::cout << x << '\n';
}

This code outputs the expected result (10 11 12 13). But is this guaranteed, or does it have undefined behavior? Do I need to protect some array elements with mutexes or anything else, so they are only ever written once?

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 3
    UB: See https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races – Mike Vine Oct 13 '20 at 12:08
  • 1
    Simplest way to fix this is to have an array of `atomic` and use one of the weaker memory orders. – Mike Vine Oct 13 '20 at 12:09
  • 1
    writing the same value does not help, because writing to an `int` is not (necessarily) atomic – 463035818_is_not_an_ai Oct 13 '20 at 12:09
  • 1
    @idclev463035818 even if `int` is not atomic (artificial example: the n bytes of the `int` are written separately) what could go wrong if the values written are the same? – Jabberwocky Oct 13 '20 at 12:12
  • 3
    @Jabberwocky ok, not being atomic isnt the point. The point is just that the standard says that it is UB – 463035818_is_not_an_ai Oct 13 '20 at 12:13
  • @Jabberwocky "may be written" not "are written" - most processors have instructions to write an integer in a single operation. – Adrian Cornish Oct 13 '20 at 12:14
  • @AdrianCornish yes of course, that's why I wrote _artificial example_. – Jabberwocky Oct 13 '20 at 12:18
  • @Jabberwocky, Re, "what could go wrong if..." The _official_ answer to that is, _anything_ could go wrong.\* If you need to convince somebody that your program actually is correct, and not just that it happens to work on this particular hardware, and this particular operating system, and under this particular phase of the Moon; then writing it in strict accordance with the standard is a good first step. – Solomon Slow Oct 13 '20 at 12:26
  • 3
    \* Don't underestimate the power of pernicious compiler writers to invent new optimizations that seemingly are designed to cause as much havoc as possible when your program breaks the rules. – Solomon Slow Oct 13 '20 at 12:27
  • 1
    Sorry for necroing this, but: there is the commonly accepted strict interpretation of the standard, which forbids writing the same value from two threads. However, there could be an even stricter interpretation of the standard, which allows such a program. This interpretation comes from the curious fact, that the standard uses the word "modify" instead of the more common "write" in exact this clause (6.9.2.2 afaik). Then one thread writes and the other thread... neither reads nor modifies the variable. So the question is: why does the standard use "modify" instead of "write"? – krzikalla Aug 10 '23 at 12:56

0 Answers0