C++17stladvanced
std::pmr::synchronized_pool_resource
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
A thread-safe variant of `unsynchronized_pool_resource` that uses internal synchronization to allow concurrent allocations from multiple threads. It provides pool-based allocation with per-thread caching for reduced contention.
Real-World Scenario
Share a pool allocator across worker threads in a multi-threaded request handler.
Quick Snippet
snippet.cpp
#include <memory_resource>
#include <vector>
#include <thread>
std::pmr::synchronized_pool_resource pool;
void worker() {
std::pmr::vector<int> data(&pool);
for (int i = 0; i < 1000; ++i) data.push_back(i);
}
std::thread t1(worker), t2(worker);
t1.join(); t2.join();Full Example
synchronized_pool_resource.cpp
// CodeNebula - C++17 Example: std::pmr::synchronized_pool_resource
// Compile: g++ -std=c++17 -Wall -Wextra -o example synchronized_pool_resource.cpp -lpthread
// Demonstrates std::pmr::synchronized_pool_resource, a thread-safe pool-based
// memory resource. Multiple threads can allocate and deallocate concurrently.
// It uses internal synchronization, making it safe without external locking.
#include <iostream>
#include <memory_resource>
#include <vector>
#include <thread>
#include <mutex>
#include <numeric>
std::mutex cout_mutex;
void worker(int id, std::pmr::memory_resource* pool, int count) {
// Each thread allocates its own pmr::vector from the shared pool
std::pmr::vector<int> data(pool);
for (int i = 0; i < count; ++i) {
data.push_back(id * 1000 + i);
}
int sum = std::accumulate(data.begin(), data.end(), 0);
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << " Thread " << id << ": " << data.size()
<< " elements, sum = " << sum << std::endl;
}
int main()
{
// ------------------------------------------------------------------
// 1. Basic thread-safe pool
// ------------------------------------------------------------------
std::cout << "=== Synchronized Pool (Multi-threaded) ===" << std::endl;
std::pmr::synchronized_pool_resource pool;
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back(worker, i, &pool, 100);
}
for (auto& t : threads) t.join();
// ------------------------------------------------------------------
// 2. Custom pool options
// ------------------------------------------------------------------
std::cout << "\n=== Custom Options ===" << std::endl;
std::pmr::pool_options opts;
opts.max_blocks_per_chunk = 32;
opts.largest_required_pool_block = 1024;
std::pmr::synchronized_pool_resource custom_pool(opts);
std::pmr::vector<int> v(&custom_pool);
for (int i = 0; i < 200; ++i) v.push_back(i);
std::cout << " max_blocks_per_chunk = 32" << std::endl;
std::cout << " largest_required_pool_block = 1024" << std::endl;
std::cout << " Stored " << v.size() << " elements" << std::endl;
// ------------------------------------------------------------------
// 3. Comparing synchronized vs unsynchronized
// ------------------------------------------------------------------
std::cout << "\n=== Synchronized vs Unsynchronized ===" << std::endl;
std::cout << " synchronized_pool_resource:" << std::endl;
std::cout << " - Thread-safe (internal locks)" << std::endl;
std::cout << " - Slightly slower due to synchronization" << std::endl;
std::cout << " - Use when multiple threads share a pool" << std::endl;
std::cout << " unsynchronized_pool_resource:" << std::endl;
std::cout << " - NOT thread-safe" << std::endl;
std::cout << " - Faster (no locking overhead)" << std::endl;
std::cout << " - Use for single-threaded or thread-local pools" << std::endl;
// ------------------------------------------------------------------
// 4. Multiple threads with concurrent alloc/dealloc
// ------------------------------------------------------------------
std::cout << "\n=== Concurrent Alloc/Dealloc ===" << std::endl;
std::pmr::synchronized_pool_resource shared_pool;
auto mixed_work = [&shared_pool](int id) {
for (int round = 0; round < 5; ++round) {
std::pmr::vector<int> temp(&shared_pool);
for (int i = 0; i < 50; ++i) temp.push_back(i);
// temp destroyed: memory returned to pool
}
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << " Thread " << id << " completed 5 alloc/dealloc rounds" << std::endl;
};
std::vector<std::thread> workers;
for (int i = 0; i < 4; ++i) {
workers.emplace_back(mixed_work, i);
}
for (auto& t : workers) t.join();
std::cout << " All threads completed safely" << std::endl;
return 0;
}
/*
Expected output (thread ordering may vary):
=== Synchronized Pool (Multi-threaded) ===
Thread 0: 100 elements, sum = 4950
Thread 1: 100 elements, sum = 104950
Thread 2: 100 elements, sum = 204950
Thread 3: 100 elements, sum = 304950
=== Custom Options ===
max_blocks_per_chunk = 32
largest_required_pool_block = 1024
Stored 200 elements
=== Synchronized vs Unsynchronized ===
synchronized_pool_resource:
- Thread-safe (internal locks)
- Slightly slower due to synchronization
- Use when multiple threads share a pool
unsynchronized_pool_resource:
- NOT thread-safe
- Faster (no locking overhead)
- Use for single-threaded or thread-local pools
=== Concurrent Alloc/Dealloc ===
Thread 0 completed 5 alloc/dealloc rounds
Thread 1 completed 5 alloc/dealloc rounds
Thread 2 completed 5 alloc/dealloc rounds
Thread 3 completed 5 alloc/dealloc rounds
All threads completed safely
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example synchronized_pool_resource.cpp -lpthread