'C++ how to safely multi thread read and write a memory block?
- reader threads can read the memory block simultaneously but not while a writer thread is writing.
- only one writer thread can write at a time.
- a writer thread can not write while a reader is reading.
How to do it in C++?
Solution 1:[1]
Here's a simple example to get you started:
- It uses a global buffer of 5 ints, and a global
std::mutex. - It creates 5 reader threads and 5 writer threads, each of which, respectively, go on and read/write the whole buffer and print what they read/wrote and from/to where.
- The threads' life overlap: readers and writers creation is interleaved and, apart from that, each of them waits a random time before starting to work. Reader threads also wait some random time after the read.
- All the threads share a
std::shared_mutex. - Each reader thread uses a
std::shared_lockon thestd::shared_mutex, allowing simultaneous reads; while each writer thread uses astd::unique_lockon thestd::shared_lock, granting them exclusive access to the buffer.
#include <chrono>
#include <fmt/core.h>
#include <iostream> // cout
#include <random> // default_random_engine, random_device, uniform_int_distribution
#include <thread> // this_thread
#include <vector>
#include <mutex> // unique_lock
#include <shared_mutex> // shared_lock, shared_mutex
constinit const size_t buffer_size{5};
constinit int buffer[buffer_size]{};
constinit std::shared_mutex buffer_mtx{};
auto random_sleep_for = [](int low, int high) {
std::default_random_engine eng{std::random_device{}()};
std::uniform_int_distribution<> dist{low, high};
std::this_thread::sleep_for(std::chrono::milliseconds{dist(eng)});
};
auto reader = [](int i){
for (size_t n{0}; n < buffer_size; ++n) {
random_sleep_for(50, 100);
std::shared_lock lck{buffer_mtx}; // shared access with other readers
std::cout << fmt::format("Thread {} reading {} from buffer[{}]\n",
i, buffer[n], n);
random_sleep_for(50, 100);
std::cout << fmt::format("Thread {} done reading {} from buffer[{}]\n",
i, buffer[n], n);
}
};
auto writer = [](int i){
for (size_t n{0}; n < buffer_size; ++n) {
random_sleep_for(40, 80);
std::unique_lock lck{buffer_mtx}; // exclusive access to buffer
std::cout << fmt::format("\tThread {} writing {} to buffer[{}]\n",
i, i*(n+1), n);
buffer[n] = i*(n+1);
std::cout << fmt::format("\tThread {} done writing {} to buffer[{}]\n",
i, i*(n+1), n);
}
};
int main()
{
std::vector<std::thread> writers{};
std::vector<std::thread> readers{};
for (int i{0}; i < 5; ++i) {
writers.emplace_back(writer, i + 1);
readers.emplace_back(reader, i + 1);
}
for (int i{0}; i < 5; ++i) {
writers[i].join();
readers[i].join();
}
}
// Outputs:
//
// Thread 5 writing 5 to buffer[0]
// Thread 5 done writing 5 to buffer[0]
// Thread 1 writing 1 to buffer[0]
// Thread 1 done writing 1 to buffer[0]
// Thread 5 reading 1 from buffer[0]
// Thread 4 reading 1 from buffer[0]
// Thread 3 reading 1 from buffer[0]
// Thread 2 reading 1 from buffer[0]
// Thread 1 reading 1 from buffer[0]
// Thread 5 done reading 1 from buffer[0]
// Thread 3 done reading 1 from buffer[0]
// Thread 2 done reading 1 from buffer[0]
// Thread 4 done reading 1 from buffer[0]
// Thread 1 done reading 1 from buffer[0]
// ...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
