5 ms·
Easier to reason about. easier to communicate. Threads are a low level primitive. the goroutines seemed to be designed with concurrency in mind.
by b0sk 6y ago
Easier to reason about. easier to communicate. Threads are a low level primitive. the goroutines seemed to be designed with concurrency in mind.
- monocasa 6y agoIt's the channels that make goroutines easier to reason about, not the green threads. There's plenty of channel implementations on native threads.
- dilyevsky 6y agoExample of c++ channel library that is easy to use? The only one I’ve seen was packaged with Google’s internal cpp fiber library that depended on custom linux kernel patches.
- monocasa 6y agoI haven't done much actor model programming in C++ in nonproprietary envs so I'm not sure there. But Rust's mpsc package is a good public example of the concept off the top of my head running on native threads. They're really not that complicated and a c++ version would be on the order of a few hundred lines. I had been using the concept since the 90s, as RTOSes really love it. Pretty much every N64 game uses native threads communicating via "software FIFOs".
- harikb 6y agoRust one you mention is multiple-producer single-consumer. And it is not nearly as feature rich as a channel in Go. By the time one deals with tokio, async/await, passing the (rx,tx) to the right thread with clone()/copy(), the developer would be tired. There is a huge difference in library implementations of channel vs native language support
- monocasa 6y agoYou don't need any tokio or async/await to use mpsc. It's in the standard library and works just fine with a thread spawned with a closure to work on. It's split into a read half and a write half so you don't have to worry about copy or clone, as an execution context will only have one or the other. I guess you clone the write half to give it to multiple producers, but that's not a huge deal. The example here for instance doesn't look any more complicated than using go channels, and it's a full executable example you can start by clicking the run button. https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html
- masklinn 6y ago> Rust one you mention is multiple-producer single-consumer. And it is not nearly as feature rich as a channel in Go. By the time one deals with tokio, async/await, passing the (rx,tx) to the right thread with clone()/copy(), the developer would be tired. tokio and async/await have little to do with channels (other than having their own async-aware versions). If you need a real mpmc channel, crossbeam[0] provides one. The interface is similar to the stdlib's, but you can clone the receiver as well as the sender. [0] https://docs.rs/crossbeam/0.8.0/crossbeam/channel/index.html https://docs.rs/crossbeam/0.8.0/crossbeam/channel/index.html
- harikb 6y agotokio use is often not by choice. web service might need hyper.rs, which uses tokio underneath. Put a crossbeam non-async call in between and one would bring concurrency to a stop.
- chubot 6y agoAn issue with doing that kind of programming in C++ is ownership. Basically if you have goroutines/threads and channels, then GC is helpful. So the Go language is really giving you 2 things together that C++ doesn't have that makes concurrent programming easier. But arguably they could have just given you threads and channels and GC, and not goroutines. That is, leaving out M:N threading. (I thought gccgo did that a long time ago or still does?) You can use threads and channels (which are just thread-safe queues) in C++, and follow a convention where you NULL out pointers after passing them across a channel. So basically you have single ownership. That is all a manual process, which is OK sometimes, but when you add in threads and the possibility of nondeterministic bugs, then maybe it's easier to see why that style of programming is not super popular. Though I have done it for small programs and it works well.
- monocasa 6y agoI'm not sure a GC really helps that much. Yes, there's a few memory unsafety bugs from ownership issues fixed, but you still need to forcibly forget about any references you had to the data being passed along the channel so you don't access it concurrently (which is the case where go's memory safety guarantees start breaking down too, concurrent access to mutable pointers).
- chubot 6y agoYeah that's a fair point. Well then you don't need much to implement a thread-safe queue in C++, and that's a big step toward Go-style concurrency. If you want to be really minimal you could just use pipe() with each end in a different thread but the same process, even passing pointers over it. Probably the bigger deal is the consistent networking libraries, and the consistent mechanisms for timeouts and cancellation that Go offers. I think that's difficult in most C++ programs using threads. (I'd be interested to know what C++ programs do that well) On a project several years back, that's what pushed me toward async -- doing timeouts in a principled way. Though to be fair to Go, I would say that memory corruption bugs can be highly non-local (from symptom to code fix), and race conditions less so. Bugs that have both properties are really painful and I think Go mostly helps you avoid the former.