15

Having several threads running I need to guaranty that every of my threads reached a certain point before proceeding. I need to implement a kind of barrier. Consider a function func which can be run from several threads:

void func()
{
  operation1();
  // wait till all threads reached this point 
  operation2();
}

What is best way to realise this barrier using C++ 11 and VS12, considering boost if needed.

UmNyobe
  • 22,539
  • 9
  • 61
  • 90
GregPhil
  • 475
  • 1
  • 8
  • 20
  • Simple way would be to separate work to two different functions, in main thread create threads to run the first half, join all of them and then run second half. – Zereges Aug 17 '16 at 15:01
  • This would be actually to complicated. My architecture doesn't allow me the to separate the work. – GregPhil Aug 17 '16 at 15:15
  • 1
    There's an actual thread primitive called barrier that is used for this. I know pthread implements this. Also, boost implements this. – Anon Mail Aug 17 '16 at 16:21
  • 1
    The answers to [this question](http://stackoverflow.com/questions/24465533/implementing-boostbarrier-in-c11) seem to cover this nicely. – Ami Tavory Aug 18 '16 at 09:46

3 Answers3

14

You could use boost::barrier
Unfortunately, the thread barrier concept itself is not part of c++11 or visual c++.
In pure c++11 you could use a condition variable and a counter.

#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>

class my_barrier
{

 public:
    my_barrier(int count)
     : thread_count(count)
     , counter(0)
     , waiting(0)
    {}

    void wait()
    {
        //fence mechanism
        std::unique_lock<std::mutex> lk(m);
        ++counter;
        ++waiting;
        cv.wait(lk, [&]{return counter >= thread_count;});
        cv.notify_one();
        --waiting;
        if(waiting == 0)
        {
           //reset barrier
           counter = 0;
        }
        lk.unlock();
    }

 private:
      std::mutex m;
      std::condition_variable cv;
      int counter;
      int waiting;
      int thread_count;
};

int thread_waiting = 3;
my_barrier barrier(3);


void func1()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
    barrier.wait();
    std::cout << "I have awakened" << std::endl;
}

void func2()
{
    barrier.wait();
    std::cout << "He has awakened!!" << std::endl;
}

int main() {
    std::thread t1(func1);  
    std::thread t2(func2);
    std::thread t3(func2);
    t1.join();
    t2.join();
    t3.join();
}

Each thread wait till a predicate is met. The last thread will make the predicate valid, and allow the waiting threads to proceed. If you want to reuse the barrier (for instance call the function multiple times), you need another variable to reset the counter.

This current implementation is limited. A calling func();func(); twice may not make threads wait the second time.

UmNyobe
  • 22,539
  • 9
  • 61
  • 90
  • Thx a lot... look straightforward and very promising! – GregPhil Aug 18 '16 at 09:58
  • 4
    With your example implementation of `wait`; Imagine a barrier for 2 threads - and they both call `wait`, the thread calling wait for the 2nd count will make progress. But since the thread who called `wait` for the 1st count is never woken up (`notified`), it may wait forever in theory. – WhiZTiM Dec 16 '17 at 00:07
3

An option could be the use of OpenMP framework.

#include <omp.h>

void func()
{
  #pragma omp parallel num_threads(number_of_threads)
  {
    operation1();

    #pragma omp barrier
    // wait till all threads reached this point 

    operation2();
  }
}

Compile the code with -fopenmp

tnas
  • 510
  • 5
  • 16
  • sounds interesting, is there any documentation/reference how to use omp? – GregPhil Aug 17 '16 at 15:13
  • A good introductory tutorial is here [openmp tutorial](http://bisqwit.iki.fi/story/howto/openmp/) and references are here [openmp reference](https://computing.llnl.gov/tutorials/openMP/). An excelent forum is here [openmp forum](http://openmp.org/forum/) – tnas Aug 17 '16 at 15:21
  • That sounds not portable – Zereges Aug 17 '16 at 15:24
1

Solution:

#include <cassert>
#include <condition_variable>

class Barrier
{

public:

    Barrier(std::size_t nb_threads)
        : m_mutex(),
        m_condition(),
        m_nb_threads(nb_threads)
    {
        assert(0u != m_nb_threads);
    }

    Barrier(const Barrier& barrier) = delete;

    Barrier(Barrier&& barrier) = delete;

    ~Barrier() noexcept
    {
        assert(0u == m_nb_threads);
    }

    Barrier& operator=(const Barrier& barrier) = delete;

    Barrier& operator=(Barrier&& barrier) = delete;

    void Wait()
    {
        std::unique_lock< std::mutex > lock(m_mutex);

        assert(0u != m_nb_threads);

        if (0u == --m_nb_threads)
        {
            m_condition.notify_all();
        }
        else
        {
            m_condition.wait(lock, [this]() { return 0u == m_nb_threads; });
        }
    }

private:

    std::mutex m_mutex;

    std::condition_variable m_condition;

    std::size_t m_nb_threads;
};

Example:

#include <chrono>
#include <iostream>
#include <thread>

Barrier barrier(2u);

void func1()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
    barrier.Wait();
    std::cout << "t1 awakened" << std::endl;
}

void func2()
{
    barrier.Wait();
    std::cout << "t2 awakened" << std::endl;
}

int main()
{
    std::thread t1(func1);  
    std::thread t2(func2);
    t1.join();
    t2.join();

    return 0;
}

Try It Online: WandBox

Matthias
  • 4,481
  • 12
  • 45
  • 84