"If you are writing multithreaded programs, you _will_ have locks, period."
Not necessarily. In scientific computing, it is common to encounter "embarrassingly parallel" scenarios where each thread can solve equal-sized chunks of the problem, and you can wait till all threads have terminated to collect results.
edit: Okay, the final wait is usually implemented with a lock under the hood. Nevertheless, this type of program is straightforward to reason about.
Here is where you need to be really careful. How do you know all the threads have terminated? That's communicated through a lock in the underlying .
Certainly some systems (such as Java and multiple CPUs with independent caches) that use thread-local working areas, you cannot rely on a task being completed meaning that you can actually use the data it has produced, until you actually make use of a lock. The lock ensures that the local copy is synchronised with the central copy.
There's plenty of opportunity for fun if you don't use locks in some form or other in multi-threaded systems.
Who cares how it's implemented? Manually using a lock is a smell in the same way that goto is a smell. Wait for a task to finish is not a smell in the same way that while is not a smell.
"Manually using a lock is a smell in the same way that goto is a smell."
The presence of a lock tells you nothing about whether a program is well-designed. In fact, the whole idea that you can argue about the correctness of a program from the presence or absence of "code smells" is absurdly simplistic. I have seen plenty of bad code littered with while statements.
Code smells (I don't like the name either) are just warning signs. They do not necessarily indicate an actual problem.
Incorrect use of abstraction levels, such as using tools that are too low-level and error-prone for the task at hand (as in the majority of business applications) IS one of those warning signs. It usually indicates a conceptual problem, and makes the code needlessly complex.
There isn't really that big of a difference between a lock and a wait, which is the point that was being made. It either way will "lock" up the thread until condition X is full filled and since it depends on other threads to finish, it's still very possible to get deadlocks and a like.
Besides that, depending on the task, it's very important how something is implemented.
And if both threads are doing a blocking read waiting for their message? Blocking locks, deadlocks, endless fun...
But wait, you don't use blocking-IO, you poll the sockets to see if there is data available, right? Do you realize what you are polling? (hint, it is global and mutable)
Not necessarily. In scientific computing, it is common to encounter "embarrassingly parallel" scenarios where each thread can solve equal-sized chunks of the problem, and you can wait till all threads have terminated to collect results.
edit: Okay, the final wait is usually implemented with a lock under the hood. Nevertheless, this type of program is straightforward to reason about.