Using std::ranges::subrange with Multi-threaded Code

How does std::ranges::subrange interact with multi-threaded code?

When using std::ranges::subrange in multi-threaded code, it's important to understand how subranges interact with shared data.

Subranges themselves are lightweight and non-owning, meaning they do not manage the lifetime of the underlying data. This can lead to potential issues if the underlying container is modified concurrently.

Read-Only Access

If your std::ranges::subrange is only being used for read-only access, you can safely use it across multiple threads.

Each thread can iterate over the subrange without modifying the underlying data. For example:

#include <iostream>
#include <ranges>
#include <thread>
#include <vector>

void print_subrange(std::ranges::subrange<
  std::vector<int>::iterator> view) {
  for (int n : view) {
    std::cout << n << ", ";
  }
}

int main() {
  std::vector<int> Nums{1, 2, 3, 4, 5};
  std::ranges::subrange view{Nums};

  std::thread t1(print_subrange, view);
  std::thread t2(print_subrange, view);

  t1.join();
  t2.join();
}
1, 2, 3, 4, 5, 1, 2, 3, 4, 5,

Concurrent Modification

If you need to modify the underlying container while using a subrange, you must ensure proper synchronization to avoid data races.

This typically involves using mutexes or other synchronization primitives. For example:

#include <iostream>
#include <mutex>
#include <ranges>
#include <thread>
#include <vector>

std::mutex mtx;

void modify_subrange(std::ranges::subrange<
  std::vector<int>::iterator> view) {
  std::lock_guard<std::mutex> lock(mtx);
  for (int& n : view) {
    n += 1;  
  }
}

int main() {
  std::vector<int> Nums{1, 2, 3, 4, 5};
  std::ranges::subrange view{Nums};

  std::thread t1(modify_subrange, view);
  std::thread t2(modify_subrange, view);

  t1.join();
  t2.join();

  for (int n : Nums) {
    std::cout << n << ", ";
  }
}
3, 4, 5, 6, 7,

Guidelines

  • Read-Only Access: Safe to use across multiple threads without additional synchronization.
  • Modifications: Ensure proper synchronization when modifying the underlying container.
  • Lifetime Management: Ensure the underlying container outlives the subranges.

Summary

Using std::ranges::subrange in multi-threaded code can be efficient and powerful when done correctly. Always consider the synchronization requirements and the lifetime of the underlying data to avoid race conditions and undefined behavior.

Creating Views using std::ranges::subrange

This lesson introduces std::ranges::subrange, allowing us to create non-owning ranges that view some underlying container

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Handling Out-of-Bounds Errors in std::ranges::subrange
How do I handle out-of-bounds errors when accessing elements in a std::ranges::subrange?
Difference between std::ranges::subrange and std::span
What is the difference between std::ranges::subrange and std::span?
Converting std::ranges::subrange to Original Container Type
How can I convert a std::ranges::subrange back to its original container type?
Using std::ranges::subrange with Non-Standard Containers
Can I use std::ranges::subrange with non-standard containers?
Ensuring Lifetime Management in std::ranges::subrange
How do I ensure the lifetime of the container outlives the std::ranges::subrange?
Advantages of std::ranges::subrange Over Raw Pointers
What are the advantages of using std::ranges::subrange over raw pointers?
Creating std::ranges::subrange from Multiple Containers
Can I use std::ranges::subrange to view data from multiple containers simultaneously?
Modifying Containers through std::ranges::subrange
Can std::ranges::subrange be used with standard library algorithms that modify the container?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant