But yeah I mean, if you mean it's pretty easy to call pthread_create then OK. But if you at all care about your program working, then parallel code is much, much harder to write.
The issues related to sharing state can be easily avoided by, for example, using a CSP-style paradigm (channels in Go). In Go, this only leaves behind a deadlock, which are automatically panic, making it incredibly easy to debug.
Overhead of scheduling and effects on optimizers are not related to concurrency/parallelism. The scheduler is affected by many things even under single-threaded execution which is far beyond the scope of normal application developers, and optimizers are largely unaffected by concurrent/parallel programming (although shared structures must internally present memory barriers). The optimizer is also beyond the scope of normal application development.
If you are doing development that requires precise control over optimizations and scheduling (like I do), then all bets are off.
However, with "normal" (i.e. not-processing-8x100Gb/s streams) programming, concurrent programming is a breeze unless you intentionally shoot yourself in the foot.
Well but for almost all of those problems you have something to help you. Yes manual memory management is hard, thanks GC/lifetimes. Yes error handling is hard, thanks exceptions (kind of). The point of OP is that practically all of the things we've built to help us with parallel/concurrent programming are still very low level, and your program still has to deal with the fallout in a way that it doesn't with GC or exceptions or other helpful abstractions.
> The issues related to sharing state can be easily avoided by, for example, using a CSP-style paradigm (channels in Go). In Go, this only leaves behind a deadlock, which are automatically panic, making it incredibly easy to debug.
There are only a few languages/platforms where CSP is feasible, and very few (none?) of them achieve the performance of CSP in Go because they don't/can't use segmented stacks. And even when using CSP you still need synchronization primitives. Parallel Go code uses mutexes everywhere. CSP in Go helps with _implementing_ parallel programs, but the Go team provides a lot of extra tooling to help _debug_ parallel programs that's well outside the definition of CSP --> the deadlock panic you cite is a good example actually.
> Overhead of scheduling and effects on optimizers are not related to concurrency/parallelism.
I disagree; mostly my evidence is "just google for volatile in C".
~~~
In general I still think parallel program is in a separate class of problems, but even if we stipulate it's just as hard as other problems, I still think we have much better tools for dealing with the other problems. I think we're getting there -- and CSP is part of that -- but it's definitely not the case that we've settled on a solution for 99% of problems.
[citation needed]. What in the world is this "fallout"?
You can of course chose not to use CSP or some other abstraction, but that would be like chosing to do manual memory management (which you can also do in Go through cgo if you'd like), and the "fallout" is identical: You are on your own. CSP is of course not magic, but neither is a GC or lifetimes. You must always know the tools you are working with, as they have their own issues that must be dealt with.
If we look at the concerns in the article, Rust's lifetime actually remove those issues entirely, much in line with its "fearless concurrency" motto. Go's CSP is opt-in-although-aggressively-recommend, and is by no means a low-level construct.
A compare-and-swap is a "low-level" construct. You can of course pull in atomic primitives if you feel like doing so, but then you are choosing to go low-level.
Also, exceptions are a terrible, terrible thing. They make error handling much, much worse.
> There are only a few languages/platforms where CSP is feasible, and very few (none?) of them achieve the performance of CSP in Go because they don't/can't use segmented stacks
I do not see how this claim makes sense. Segmented stacks are an unnecessary green thread implementation detail that can both harm and benefit performance (if you use FFI, they harm performance a lot), and is neither necessary for green threads, nor related to CSP at all.
CSP works just fine in other languages. You can fully execute a CSP paradigm in C, just like you can avoid it altogether in Go and Rust.
Although, while you can avoid CSP in Rust, you still can't create the issues presented in the articles due to lifetimes. You would have to quite explicitly and intentionally get your hands dirty with unsafe code in order to shoot yourself in the foot here.
> I disagree; mostly my evidence is "just google for volatile in C".
"volatile" has nothing to do with anything here. Not only that, it is an entirely useless construct, as it does not provide reordering guarantees (that is, while subsequent reads see a previous write, the reads and writes may be reordered so that the reads now happen before the write). Proper constructs use memory barriers.
However, neither of these constructs have bad effects on optimization, unless you are aiming for code that executes fast without working at all. Also, memory barriers are low-level primitives. Unless you are designing synchronization primitives, you shouldn't touch them.
"volatile" and memory barriers have no effect on process scheduling at all. It does, however, necessarily affect the CPU instruction pipeline, but once you get your hands this dirty, you're way out of the scope of high-level programming safety net. Down here, we work with assembly.