1

I have the following situation.

I have a thread that writes to an array. After writing to that array I need to read that array. Reading happens after writing the array and I can guarantee so through another means (this would imply I will not need condition variables, but just when reading "refreshing" contents of the array). The array is read later from another thread.

EDIT: Additionally, the writing will happen at different times, the array is not written all at once.

I need to read the full contents of the array with all entries already refreshed and with the actual values at the time of reading. Right now the values are not refreshed.

What is the best way to synchronize this?

  • mutex?
  • atomic?
  • fences?

I am not sure how I would do it. I do know for a single variable a mutex would be enough, but this is an array. I am not sure what the correct way to do this is. I am not sure how to do it: a lock?

Germán Diago
  • 7,473
  • 1
  • 36
  • 59
  • If you are sure that no more writes will be done to the array, no synchronization at all is needed. Multiple readers only don't need protections. – Some programmer dude Jun 22 '16 at 03:00
  • No more writes will be done, but I do not see the data updated from the reader thread, so I assume I am missing some synchronization. – Germán Diago Jun 22 '16 at 03:30
  • For simplicity, I recommend you use `mutex`. You lock when start to writte in the array and unlock when finish and the same for reading the array. – chema989 Jun 22 '16 at 03:35
  • 1
    Using `std::atomic_flag` is easy too. You `clear` the flag when finish to writte the array and `test_and_set` the flag when start to read the array. – chema989 Jun 22 '16 at 03:42
  • It sounds like you just need to detect when the writer is finished, and don't let the reader(s) start until then. @chema989's `atomic_flag` idea is close, but I'd use a `std::condition_variable` instead. That way, the reader can sleep (instead of busy-wait) until the writer is finished. – Dave M. Jun 22 '16 at 03:59
  • 1
    @DaveM. Yes German commented: `After writing to that array I need to read that array`, the unique condition is that reading is done from another thread and don't do writting and reading in parallel. But `std::condition_variable` is a good choice too. – chema989 Jun 22 '16 at 04:44
  • Yes, I need to write, but I will need to lock 4 times. Writing to the array is not done at once. It is done asynchronously at different times. I will add this information to the post – Germán Diago Jun 22 '16 at 04:45
  • @GermánDiago : `std::atomic_uint` instead of `std::atomic_flag` then, initialized to the number of writers with each writer decrementing when finished and the reader waiting for `0`. – ildjarn Jun 22 '16 at 05:27
  • @ildjarn Actually I did not specify well enough the problem before, so maybe my original statement does not hold anymore: I need to read, but I am not waiting from the other thread. I just want, at a certain point, read and, since I know the data is already written, synchronize. I think I should not need condition variables or so, what I need is to "refresh" the memory from the other thread to make it visible. A mutex will do for sure, but is there another way? Another characteristic is that the writing thread writes each of the array entries at possibly different times. – Germán Diago Jun 22 '16 at 06:16
  • @GermánDiago : I'm not talking about using `condition_variable` at all. The reader thread still needs to atomically read an understood value (`0` in this case) in order for the C++ memory model to guarantee the array contents' visibility. This must necessarily be implemented as a loop, though given your described scenario, it's only likely to iterate once. – ildjarn Jun 22 '16 at 06:44
  • If, as you say, you are only reading the values after they have been written, you don't need any syncronisation at all. All you need is to declare the data as `volatile` which will prevent the compiler from storing the data in potentially stale registers. – Mark Setchell Jun 22 '16 at 09:49

2 Answers2

2

There is a fundamental issue that an array cannot be written to and read atomically. Without blocking the writer and the reader may race, so that the reader may observe a partially updated array.

One solution would be to use a single-producer-single-consumer ring-buffer with elements being pointers to those arrays. This way the writer and the reader do not race writing/reading the same array and the reader only ever observes consistent data.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
1

If I understand the question correctly, you know that you read some values AFTER another thread changed the value, but you don't see the change.

In fact it is not guaranteed that your reading thread will read the updated value of the writing thread without some synchronisation mechanism (you can read more here).

A solution that would work is to use atomic variables, for instance if you have an array of ints, change it to an array of atomic

std::atomic<int> arr[4];
Community
  • 1
  • 1
gbehar
  • 1,229
  • 10
  • 10
  • My array looks more like this std::array. I think that will make a difference. I am not sure yet as to which is the best way to sync the data, but maybe if a mutex will do, I should just do that. – Germán Diago Jun 22 '16 at 10:50
  • Well, std::array is much better then c style arrays, but it would work the same. The solution with atomic is good because it would work without locking, a little bit like volatile in java. – gbehar Jun 22 '16 at 10:51
  • No, what I mean is that I cannot have a std::atomic. Is that accurate? – Germán Diago Jun 22 '16 at 10:52
  • I see now what you mean, if it's specific fields in the non atomic struct, you could maybe just make those atomic. Otherwise if mutex works for you, perhaps it's better (and more readable) – gbehar Jun 22 '16 at 10:54
  • 1
    I am gonna go for a mutex and expose a function through a class that receives a lambda and operates on the structure. I think it will be easier and the array holds little data. That should do it. – Germán Diago Jun 22 '16 at 11:03
  • @GermánDiago : You don't need to make your data atomic, nor do you need a mutex; you just need a release fence on _some_ atomic variable after the data is written and an acquire fence on said atomic before it's read. If you'd post an [SSCCE](http://sscce.org/)/MVCE it would be very easy to demonstrate concretely. – ildjarn Jun 22 '16 at 20:02