4 ms·
Rust prevents data races with the `Send` and `Sync` markers, but that's a long way from making concurrency and parallelism easy. In Go, as long as you are not
by the_duke 7y ago
Rust prevents data races with the `Send` and `Sync` markers, but that's a long way from making concurrency and parallelism easy.
In Go, as long as you are not sharing data, you don't really have to care about whether something is concurrent or parallel, the runtime takes care of it for you. You just use goroutines, IO transparently happens on a dedicated threadpool, CPU heavy tasks are spread out over the available cores.
As long as you restrict sharing to sending owned data over channels, you are mostly in the clear. Of course sometimes performance concerns etc mandate more traditional tools (atomics, mutexes, etc) and there the compiler won't help you anymore, but I'd argue that is the exception in Go rather than the norm.
You can often solve problems with a actor-system inspired design. (learning Erlang/Elixir will probably make you a better Go developer!)
In Rust, you still have to care a lot about what you do. Things like tokio::fs can handle file system IO for you, but you need to ensure that code you are calling does no synchronous IO.
Also, CPU heavy workloads should be offloaded to a dedicated CPU thread pool, since the tokio runtime is not intended for juggling CPU intensive tasks in addition to the async IO. This is fine for Rust, a language that wants to give you insight and control, but it is still much more demanding of the developer.
- pcwalton 7y ago> You just use goroutines, IO transparently happens on a dedicated threadpool, CPU heavy tasks are spread out over the available cores. In Rust you can do that too! Just use threads. That's what Go does: it just uses threads. Async I/O is there if you need better performance than what Rust—or Go—can provide. If you don't need that performance, then don't use async I/O.
- foldr 7y agoGo uses async IO under the hood (for network IO at least). The exposed API is blocking, but the underlying implementation uses async IO and transparently schedules Goroutines across different OS threads. As you know, Rust doesn't currently have stable support for a similarly high-level interface to async IO. See e.g. the following reddit thread from 3 months ago: https://www.reddit.com/r/rust/comments/ausuld/what_is_the_current_state_of_async_io_in_rust/ https://www.reddit.com/r/rust/comments/ausuld/what_is_the_cu...
- pcwalton 7y agoUsing async I/O in userspace to implement the sync I/O interface of goroutines is equivalent to what the kernel does to implement sync I/O for normal threads.
- foldr 7y agoKind of, but with the difference that Goroutines are lighter than kernel threads, so you can run more of them.
- pcwalton 7y agoNot as much as you'd think. You can run thousands of kernel threads on Linux.
- foldr 7y agohttps://golang.org/doc/faq#goroutines https://golang.org/doc/faq#goroutines "It is practical to create hundreds of thousands of goroutines in the same address space. If goroutines were just threads, system resources would run out at a much smaller number." More generally, Go is hardly the first language to implement userspace threads backed by OS threads. Erlang famously does this, for example, and so does GHC: https://en.wikibooks.org/wiki/Haskell/Concurrency https://en.wikibooks.org/wiki/Haskell/Concurrency In response to your other comment, the whole point of Goroutines is that you can use them pretty freely without (usually) having to worry about how many there are. Let's say you're writing a server, and that handling one of the requests requires completing 10 tasks, each of which can run in parallel. In Go you can just spawn a Goroutine for each of those tasks. But if you were using real OS threads, then that implementation could easily end up spawning more than a few thousand threads, when the server was under significant load.
- pcwalton 7y agoThat FAQ answer is misleading. Linux supports thousands of threads just fine. If you have hundreds of thousands, you probably want true async I/O instead of having stackful threads at all. > But if you were using real OS threads, then that implementation could easily end up spawning more than a few thousand threads, when the server was under significant load. There's no inherent problem with spawning a few thousand threads in Linux.