mardi 20 août 2019

How to move/swap a std::vector efficiently and thread safe?

Imagine a thread which continuously writes to a vector of strings which is being collected every now and then by another thread (see code).

#include <string>
#include <vector>
#include <chrono>
#include <thread>
#include <iostream>
#include <cassert>

// some public vector being filled by one and consumed by another
// thread
static std::vector<std::string> buffer;

// continuously writes data to buffer (has to be fast)
static const auto filler(std::thread([] {
  for (size_t i = 0;; ++i) {
    buffer.push_back(std::to_string(i));
  }
}));

// returns collected data and clears the buffer being written to
std::vector<std::string> fetch() {
  return std::move(buffer);
}

// continuously fetch buffered data and process it (can be slow)
int main() {
  size_t expected{};
  for(;;) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    const auto fetched(fetch());
    for (auto && e : fetched) {
      size_t read(std::stoi(e));
      std::cout << read << " " << expected << std::endl;
      assert(read == expected);
      ++expected;
    }
  }
}

The provided example generally does what I want it to do but it crashes because it's not thread safe. Obvious approaches would be

  • to secure the shared vector using a lock_guard
  • using two buffers and an atomic pointer
  • using a thread safe vector implementation.

The provided scenario seems very simple to me. I don't think I need a thread safe vector because that would cover a lot more scenarios at the cost of performance.

Using a mutex or swapping between two instances of the vector seem plausible to me but I wonder if there is some solution specially made to 'atomically grab all data and leave an empty container'.

Maybe there's an obvious solution and it's just time to go to bed for me?

Aucun commentaire:

Enregistrer un commentaire