10 ms·
std::latch
- klodolph 5y agoSo many threading primitives have different names on different platforms. This is basically Go's WaitGroup, except the C++ latch can't be incremented (it's otherwise identical). I wonder why that is?
- junon 5y agoUsually such guarantees allow the implementation to use something other than atomics (e.g. memory barriers) for avoiding data races, which are more performant and can avoid contention/context switching/spinning. I too would love to know the specific reasoning though.
- vinkelhake 5y agoIt's CountDownLatch in Java.
- brundolf 5y agoCue the jokes about Java name length
- skocznymroczny 5y agoI like these names. Maybe it's a pain if you're using something like vim to program, but in a typical Java IDE or VSCode you'd just write CDL, ctrl+space, enter.
- signa11 5y agonot that per-se, but is there a CountUpLatch(...) as well ;)
- dgellow 5y agoThere is always std::barrier: https://en.cppreference.com/w/cpp/thread/barrier https://en.cppreference.com/w/cpp/thread/barrier. The C++ stdlib terminology is always so weird.
- jcelerier 5y agoDiscussion on naming from a few months ago : https://news.ycombinator.com/item?id=25902827 https://news.ycombinator.com/item?id=25902827
- Glavnokoman 5y agoYep. C++ is not good at naming things somehow. But bringing up Golang in a discussion about naming is just ridiculous. Unless you want to show that C++ is not actually the worst.
- zambal 5y agoTo me, the parent poster didn't imply that golang's naming is better, they just wondered why there's a lack of naming conventions regarding threading in general.
- Glavnokoman 5y agoAh! Thanks! You're right. I did not read it this way...
- lenkite 5y agoLatch is actually the standard name for this concurrency construct.
- msk-lywenn 5y agoIs go worst than rust at naming? It took me a while to get used to Cow and Arc... (not to mention "fn" which to me will always be the french far right political party)
- iudqnolq 5y agoI like that Rust has optimized certain bits for ease of intermediate use. Sure those could be EitherOwnedOrBorrowed and AtomicReferenceCountedPointer, but that'd be annoying to type once you know them. They're also not hard to remember if you learn the acronyms (for me, at least).
- andrekandre 5y agoEitherOwnedOrBorrowed and AtomicReferenceCountedPointer, but that'd be annoying to type once you know them. code is read more than it is written, so having those longer terms helps with reducing mental load and providing self-documenting code and with most modern ide or even text editors providing autocompletion, (imho) i think abbreviations not really neccessary (though they can be fun in the case of kotlin ^^)
- signa11 5y ago/// ---------------------------------------------------------------------------- /// latches are great for multi-threaded tests with following sequence of steps /// 1. setup test data /// 2. create a latch /// 3. create test threads each decrementing the latch-count /// 4. when all threads have reached the latch, they are unblocked. /// /// sligtly verbose commented code to illustrate this void foo() { /// -------------------------------------------------------------------- /// create a latch with non-zero-count unsigned const thread_count=...; std::latch done(thread_count); my_data data[thread_count]; std::vector<std::jthread> threads; /// -------------------------------------------------------------------- /// threads decrement count as they are created for(unsigned i=0;i<thread_count;++i) threads.push_back(std::jthread([&,i]{ data[i]=make_data(i); done.count_down(); do_more_stuff(); })); /// -------------------------------------------------------------------- /// others wait for latch to be signalled, when the count reaches zero, /// latch is permanently signalled, and threads are woken done.wait(); process_data(); } have fun ?
- klodolph 5y agoI'm just saying in Go, the equivalent construct (WaitGroup) does not require you to figure out the count ahead of time. There's an idiom of: var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() }() wg.Wait() The middle part of the block can be done freely, you don't have to count the threads beforehand.
- gpderetta 5y agoLikely minimalism. It can be implemented more efficiently if it can only count down. It is a low lever synchronization primitive, you can build more complex stuff on top of it.
- Koshkin 5y agoMore likely, due to the tendency to support pre-existing patterns, no matter the redundancy.
- threatripper 5y agoWhy does it use std::ptrdiff_t for counting instead of int?
- Google234 5y agoInt is not large enough for 64 bit system. ptrdiff Is always the correct size and is signed unlike size_t
- threatripper 5y agoAs far as I can see it's just used as a counter and doesn't really relate to pointers in any way. Why is int not large enough for this use case? EDIT: Answer from the developers here https://stackoverflow.com/a/45873222 https://stackoverflow.com/a/45873222 it's about compatibility with other types used for related purposes.
- amelius 5y agoWhat if you were counting bits instead of bytes?
- threatripper 5y agoBits don't appear in array sizes as we use bytes as smallest addressable unit on all relevant platforms.
- amelius 5y agoBut you could imagine a vector of booleans which you can subscript per boolean.
- threatripper 5y agoImaginable: Yes, Relevant: No
- 5y ago
- Agentlien 5y agoThat's a really handy thing, actually. I wasn't following the C++20 development like I usually do. A combination of no longer working with C++ and feeling a bit disillusioned by the long wait for Concepts and Modules. I did read a summary of all language proposals which made it in, but this made me realize I didn't read up on the changes to the standard library.
- banachtarski 5y agoWhat a weird article to post. Why not std::barrier, or std::binary_semaphore, or std::counting_semaphore which is also new as of C++20
- dgellow 5y agoPeople find a cool thing online and share it. Not weirder than sharing a specific wikipedia page.
- user-the-name 5y agoIt is a bit weird too how HN keeps posting random wikipedia pages.
- Koshkin 5y agoNot exactly "random," I am sure...
- ncmncm 5y agoThanks, I wrote the sample code for the std::latch and std::barrier pages. [Unfortunately you can't run them in place, because the compiler backing it is too old. I am wrong! Now you can.] But you can paste them into Godbolt.org and run there, given a bit of compiler link-line fu. std::barrier has extra knobs that I didn't discover a good use for, or would have used them in the sample. Enlightenment welcome. This might be a good place to mention: cppreference.com is curated by ISO Standard C++ committee participants. (Cplusplus.com, by contrast, is very poorly maintained.) If cppreference disagrees from the Standard, it is likely that the Standard will soon be amended to match. Speaking of Godbolt: it enforces a limit of three spawned threads, but the Gcc and Clang thread sanitizers use up a thread. So if you want to try the samples on Godbolt under the thread sanitizer, you have to reduce the problem sizes to two. Learned that the hard way.
- kitd 5y agoFWIW, I did a lot of C++ in the 1990s, then went to Java/Go for about 15 years. I had to come back to C++ briefly about 5 years ago. cppreference was a total godsend!
- diath 5y ago> Unfortunately you can't run them in place, because the compiler backing it is too old. Are you sure you've not changed the dropdown? I just made some changes to the example, hit "run this code", hit "run", and the output updated accordingly.
- ncmncm 5y agoOops, you're right -- they updated compiler support! Good news.
- klyrs 5y agoI realize that you'd be demonstrating even less of the featureset, but it seems that you could have used arrive_and_wait on a single latch.
- terryf 5y agoWow, looking at the code in the example, this looks almost nothing like the C++ I used to write 15 years ago. The language really has evolved massively and it seems in a good direction.
- vaylian 5y agoAgreed. Can someone explain the following? std::string product{"not worked"}; Searching for "curly braces C++ string" is not really productive. I'm also curious what [&](job& my_job) { } means.
- ktpsns 5y agoThe first one is a curly braces initializer. The second one is a lambda function / closure over my_job.
- dataflow 5y agoIt's called uniform initialization syntax (aka "brace initialization" aka list-initialization [1]). tl;dr is everyone loves it due to the terseness, but I recommend against it. It's kind of like a forced cast/coercion in some ways (and it also looks ugly). Also be careful with parentheses since C++20; if they invoke implicit (aggregate?) constructors that would've traditionally needed {}, I think you can also run into similar casting issues. I just ran into this one a couple days ago; I haven't narrowed it down yet, but it seemed to be due to this. The second one is a lambda (which, if you're not familiar with the term, are anonymous functions/functors); the entries in the initial brackets define the captured variables (if any), and whether they're captured by reference (with ampersand) or by value (without). [1] https://en.cppreference.com/w/cpp/language/list_initialization https://en.cppreference.com/w/cpp/language/list_initializati...
- gpderetta 5y agoIt is in fact not a forced cast, unlike T() initialization that can indeed be a cast. Uniform initialization is great, except for the unfortunate interaction with initialized_lists (yes, we can't have nice things), but if you do not have an initializer_list constructor in your class you do not have to worry.
- quietbritishjim 5y agoSee also: std::latch vs std::barrier [1] In short (copied from that answer): * Barriers are useful when you have a bunch of threads and you want to synchronise across of them at once, for example to do something that operates on all of their data at once. * Latches are useful if you have a bunch of work items and you want to know when they've all been handled, and aren't necessarily interested in which thread(s) handled them. [1] https://stackoverflow.com/a/62631294 https://stackoverflow.com/a/62631294
- quietbritishjim 5y agoTo add to this: I don't get why C++ distinguishes a latch from a barrier. Both of them are effectively a counter with the ability to decrement that counter (in a thread safe way) and the ability to wait until the value reaches zero (or return immediately if it is already zero). The only difference is that is a latch does those things separately from different threads while a barrier does them both at once in every thread. That's even reflected in the similar method names: .count_down() and .wait() on latch, and .count_down_and_wait() on barrier. Why not make all three methods be methods of one class, which simplifies the API and potentially allows other use cases?
- nly 5y agoAlmost certainly the answer is one can be implemented more efficiently than the other using a particular API on a particular platform.
- flqn 5y agoBoth latches and barriers can be implemented with a counting semaphore sure. C++20 provides one of those too along with these abstractions.
- quietbritishjim 5y agoHow could either of them be implemented with a counting semaphore? It seems like it waits in the wrong (opposite) situation than barrier and latch need, but maybe I'm just not being imaginative enough.
- testific8 5y agoI was looking to use this feature, alongside some other C++20, but I was dissapointed to see that it wasn't supported yet by my compiler. So hopefully that gets supported soon. Until then a condition variable and an atomic<bool> will do the job. I am looking forward to c++23 and beyond. Hopefully they will eventuall add fixed-point arithmetic support.