As for the GIL, while it may be convenient for organizing the codebase of the language, it is awful when it comes to using it for a heavily parallel workload. Yes, it prevents programmers from having to write code like:
with obj.lock:
obj.counter += 1
But it means that code that should by all means be lock free is now always blocking all threads. In other words, it helps really novice programmers write really simple single-user single use prototype-level code, but prevents concurrency in real-world production environments.Locks should be exposed in most languages (except environments like JavaScript which are inherently single-threaded). It's just that (1) other models such as STM should be exposes as well and (2) other abstractions built on top of or related to locks (queues, events, conditions, etc). should be available as well.
I'm no fan of GILs, but I've been burned enough times with explicit locking that I respect any attempt to find a Third Way. And why provide a lock facility if the third way is a truly usable alternative?
While locking can sometimes be a pain to reason about (having many types of locks affecting access to one piece of data, etc.), on a lot of situations they are very useful. The GIL is exactly the opposite: easy to reason about, but rarely will it save you. Consider.
x = cache['foo']
y = bar(x)
cache['foo'] = y
Here a GIL will not help here: while lines 1 and 3 are atomic and the call to bar() may or may not be atomic, there is no guarantee that the whole set of operations is atomic. Thus you need an explicit locking mechanism. A GIL is a pattern that makes implementation of interpreters simple. It does not help in problems where you may be burned by locks.On top of that, my point is that providing abstractions and the low-level interfaces is better than not providing either, or just one of the two. If a sophisticated user is not happy with how something is implemented they always have the option to implement an abstraction that eill work for them.