Le 21/04/13 13:18, Michael Marcin a écrit :
On 4/21/2013 5:45 AM, Vicente J. Botet Escriba wrote:
Le 21/04/13 10:27, Michael Marcin a écrit :
I recently ran across the need to spawn a thread and wait for it to finish its setup before continuing.
The accepted answer seems to be using a mutex and condition variable to achieve this.
However that work clutters up the code quite a bit with the implementation details.
I came across Java's CountDownLatch which does basically the same work but bundles it up into a tidy package.
What about using boost::barrier [1]?
Best, Vicente
Ah didn't know that existed. Looks like it serves a similar purpose.
Still there are a few differences.
Looks like barrier is a bit fatter to let it handle restarting on a new generation.
Usage requires you to know the number of people that are going to call wait at construction time as well.
IF you replaced the countdown_latch with barrier in my example you introduce more synchronization than is necessary. In addition to the constructor waiting on thread_func now thread_func must wait for the constructor.
You are right. After further analysis the split of the wait() count_down() of the latch class could be complementary to the current boost::barrier class. The difference been that with a latch the thread will not block on (2) while using a barrier would synchronize all the threads in (1) and (2) as the barrier::wait() is equivalent to a latch::count_down() and latch::wait() There is a C++1y proposal [1] that propose having two classes. However having two classes that are really quite close seems not desirable. The problem is that boost::barrier use already a wait() function that do the count down and synchronize up to the count is zero. I guess that the better would be to define a new set of latch classes that provides wait/count_down/count_down_and_wait. The current barrier class could be deprecated once the new classes are ready. [1] adds the possibility to reset the counter. This doesn't seems to add any complexity to the basic latch [1] adds the possibility to set a function that is called when the counter reach the value 0. This is in my opinion useful but would need an additional class. I don't think the names latch and barrier would be the good ones if the single difference is to be able to set this function. boost::barrier auto reset itself once the counter reaches the value zero. I would propose 2/3 classes Basic Latch respond to your needs and adds some more function that don't make the implementation less efficient. class latch { public: latch( latch const&) = delete; latch& operator=( latch const&) = delete; /// Constructs a latch with a given count. latch( std::size_t count ); /// Blocks until the latch has counted down to zero. void wait(); bool try_wait(); template <class Rep, class Period> cv_statuswait_for( const chrono::duration<Rep, Period>& rel_time ); template <class lock_type, class Clock, class Duration> cv_status wait_until( const chrono::time_point<Clock, Duration>& abs_time ); /// Decrement the count and notify anyone waiting if we reach zero. /// @Requires count must be greater than 0 void count_down(); /// Decrement the count and notify anyone waiting if we reach zero. /// Blocks until the latch has counted down to zero. /// @Requires count must be greater than 0 void count_down_and_wait(); /// Reset the counter /// #Requires This method may only be invoked when there are no other threads currently inside the|count_down_and_wait()| method. void reset(std::size_t count_ ); }; A completion latch has in addition to its internal counter a completion function that will be invoked when the counter reaches zero. The completion function is any nullary function returning nothing. class completion_latch { public: typedef 'implementation defined' completion_function; static const completion_function noop; completion_latch( completion_latch const& ) = delete; completion_latch& operator=( completion_latch const& ) = delete; /// Constructs a latch with a given count and a noop completion function. completion_latch( std::size_t count); /// Constructs a latch with a given count and a completion function. template <typename F> completion_latch( std::size_t count, F&& fct); /// Blocks until the latch has counted down to zero. void wait(); bool try_wait(); template <class Rep, class Period> cv_status wait_for( const chrono::duration<Rep, Period>& rel_time ); template <class lock_type, class Clock, class Duration> cv_status wait_until( const chrono::time_point<Clock, Duration>& abs_time ); /// Decrement the count and notify anyone waiting if we reach zero. /// @Requires count must be greater than 0 or undefined behavior void count_down(); /// Decrement the count and notify anyone waiting if we reach zero. /// Blocks until the latch has counted down to zero. /// @Requires count must be greater than 0 void count_down_and_wait(); /// Reset the counter with a new value for the initial count. /// #Requires This method may only be invoked when there are no other threads /// currently inside the count_down and wait related functions. /// It may also be invoked from within the registered completion function. void reset( std::size_t count ); /// Resets the latch with the new completion function. /// The next time the internal count reaches 0, this function will be invoked. /// #Requires This method may only be invoked when there are no other threads /// currently inside the count_down and wait related functions. /// It may also be invoked from within the registered completion function. /// Returns the old completion function if any or noop if template typename F> completion_function then(F&&); }; Optionally we could add a Cyclic latch provides the same interface than latch but that reset itself when zero is reached (as boost::barrier). This would be more efficient than been forced to add a completion function that reset the counter. What do you think of these interfaces? Best, Vicente [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3600.html