std::experimental::flex_barrier synchronizes a group of threads across recurring cycles. Its completion function runs on exactly one arriving thread after all participants arrive, providing a serial phase and a way to set the participant count for the next cycle. section 4.4.10 section 4.4.9
Return -1 to keep the participant count unchanged; return zero or more to specify the next cycle’s count. The programmer must ensure that exactly the appropriate number of threads will arrive. The basic barrier may have lower overhead. section 4.4.10 section 4.4.9
Listing 4.27. Using std::flex_barrier to provide a serial region
void process_data(data_source &source, data_sink &sink) {
unsigned const concurrency = std::thread::hardware_concurrency();
unsigned const num_threads = (concurrency > 0) ? concurrency : 2;
std::vector<data_chunk> chunks;
auto split_source = [&] { #1
if (!source.done()) {
data_block current_block = source.get_next_data_block();
chunks = divide_into_chunks(current_block, num_threads);
}
};
split_source(); #2
result_block result;
std::experimental::flex_barrier sync(num_threads, [&] { #3
sink.write_data(std::move(result));
split_source(); #4
return -1; #5
});
std::vector<joining_thread> threads(num_threads);
for (unsigned i = 0; i < num_threads; ++i) {
threads[i] = joining_thread([&, i] {
while (!source.done()) { #6
result.set_chunk(i, num_threads, process(chunks[i]));
sync.arrive_and_wait(); #7
}
});
}
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":6,\"ch\":63},{\"line\":6,\"ch\":65}],[{\"line\":13,\"ch\":63},{\"line\":13,\"ch\":65}],[{\"line\":17,\"ch\":63},{\"line\":17,\"ch\":65}],[{\"line\":19,\"ch\":63},{\"line\":19,\"ch\":65}],[{\"line\":20,\"ch\":63},{\"line\":20,\"ch\":65}],[{\"line\":26,\"ch\":63},{\"line\":26,\"ch\":65}],[{\"line\":28,\"ch\":63},{\"line\":28,\"ch\":65}]]"}
!@%STYLE%@!
The example moves output writing and preparation of the next data block into the completion function, leaving the loop with a single synchronization point. section 4.4.10
