liveBook · Manning

Std::experimental::flex_barrier

As explained in C++ Concurrency in Action, Second Edition

`std::experimental::flex_barrier` is an experimental C++ barrier synchronization primitive whose completion function can change the required participant count for a later cycle.

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

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Explains completion timing, return values, and a data-processing example, while leaving participant-count correctness to the programmer.
Examples
Uses chunked data processing, writing results and splitting the next block in the completion function.
What it assumes
Introduces barrier arrivals and recurring cycles before explaining the flexible completion function.
What you can do after
Apply std::experimental::flex_barrier to run serial work between parallel phases and configure the next participant count.
How it connects
Connects barrier synchronization with std::experimental::barrier and serial sections.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.