Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/barrier"

From cppreference.com
< cpp‎ | thread
(Simplify std::barrier example again)
m (Simplify std::barrier example a little more)
Line 71: Line 71:
 
   };
 
   };
  
   char const* const workers[] = { "anil", "busara", "carl" };
+
   const auto workers[] = { "anil", "busara", "carl" };
  
 
   std::barrier sync_point(std::size(workers), []{  
 
   std::barrier sync_point(std::size(workers), []{  
 
     // locking not needed here
 
     // locking not needed here
     static char const* phase = "Cleaning up...\n";
+
     static auto phase = "... done\n" "Cleaning up...\n";
     std::cout << "... done\n" << phase;
+
     std::cout << phase;
     phase = "";
+
     phase = "... done\n";
 
   });
 
   });
  
Line 95: Line 95:
 
     threads.emplace_back(work, worker);
 
     threads.emplace_back(work, worker);
 
   }
 
   }
   for (auto& thread : threads) thread.join();
+
   for (auto& thread : threads) {
 +
    thread.join();
 +
  }
 
}
 
}
 
  | p=true | output=
 
  | p=true | output=

Revision as of 02:17, 14 April 2021

 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
barrier
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
Hazard Pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11)(deprecated in C++20)
(C++11)(deprecated in C++20)
Memory ordering
Free functions for atomic operations
Free functions for atomic flags
 
 
Defined in header <barrier>
template<class CompletionFunction = /* see below */>
class barrier;
(since C++20)

The class template std::barrier provides a thread-coordination mechanism that allows at most an expected number of threads to block until the expected number of threads arrive at the barrier. Unlike std::latch, barriers are reusable: once the arriving threads are unblocked from a barrier phase's synchronization point, the same barrier can be reused.

A barrier object's lifetime consists of a sequence of barrier phases. Each phase defines a phase synchronization point. Threads that arrive at the barrier during the phase can block on the phase synchronization point by calling wait, and will be unblocked when the phase completion step is run.

A barrier phase consists following steps:

  1. The expected count is decremented by each call to arrive or arrive_and_drop.
  2. When the expected count reaches zero, the phase completion step is run. The completion step invokes the completion function object, and unblocks all threads blocked on the phase synchronization point. The end of the completion step strongly happens-before the returns from all calls that were unblocked by the completion step.
    • For the specialization std::barrier<> (using the default template argument), the completion step is run as part of the call to arrive or arrive_and_drop that caused the expected count to reach zero.
    • For other specializations, the completion step is run on one of the threads that arrived at the barrier during the phase. And the behavior is undefined if any of the barrier object's member functions other than wait are called during the completion step.
  3. When the completion step finishes, the expected count is reset to the value specified at construction less the number of calls to arrive_and_drop since, and the next barrier phase begins.

Concurrent invocations of the member functions of barrier, except for the destructor, do not introduce data races.

Contents

Template parameters

CompletionFunction - a function object type
-
CompletionFunction must meet the requirements of MoveConstructible and Destructible. std::is_nothrow_invocable_v<CompletionFunction&> must be true.

The default template argument of CompletionFunction is an unspecified function object type that addtionally meets the requirements of DefaultConstructible. Calling an lvalue of it with no arguments has no effects.

Every barrier object behaves as if it holds an exposition-only non-static data member completion_ of type CompletionFunction and calls it by completion_() on every phase completion step.

Member types

Name Definition
arrival_token an unspecified object type meeting requirements of MoveConstructible, MoveAssignable and Destructible

Member functions

constructs a barrier
(public member function) [edit]
destroys the barrier
(public member function) [edit]
operator=
[deleted]
barrier is not assignable
(public member function)
arrives at barrier and decrements the expected count
(public member function) [edit]
blocks at the phase synchronization point until its phase completion step is run
(public member function) [edit]
arrives at barrier and decrements the expected count by one, then blocks until current phase completes
(public member function) [edit]
decrements both the initial expected count for subsequent phases and the expected count for current phase by one
(public member function) [edit]
Constants
[static]
the maximum value of expected count supported by the implementation
(public static member function) [edit]

Example

#include <barrier>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
 
int main() {
  auto report = [](std::string const& s) {
    static std::mutex sync;
    std::scoped_lock lock{sync};
    std::cout << "  " << s << '\n';
  };
 
  const auto workers[] = { "anil", "busara", "carl" };
 
  std::barrier sync_point(std::size(workers), []{ 
    // locking not needed here
    static auto phase = "... done\n" "Cleaning up...\n";
    std::cout << phase;
    phase = "... done\n";
  });
 
  auto work = [&](std::string name) {
    std::string product = name + " worked";
    report(product);
    sync_point.arrive_and_wait();
 
    product = name + " cleaned";
    report(product);
    sync_point.arrive_and_wait();
  };
 
  std::cout << "Starting...\n";
  std::vector<std::thread> threads;
  for (auto const& worker : workers) {
    threads.emplace_back(work, worker);
  }
  for (auto& thread : threads) {
    thread.join();
  }
}

Possible output:

Starting...
  anil worked
  busara worked
  carl worked
... done
Cleaning up...
  busara cleaned
  carl cleaned
  anil cleaned
... done

See also

(C++20)
single-use thread barrier
(class) [edit]