I believe concurrent.futures is meant as a way for asyncio to spawn threads or processes -- which lets you essentially call sync code from async and not have it block the event loop.
From the docs[1] -
The concurrent.futures module provides a high-level interface for asynchronously executing callables.
The asynchronous execution can be performed with threads, using ThreadPoolExecutor, or separate processes, using ProcessPoolExecutor. Both implement the same interface, which is defined by the abstract Executor class.
[1] https://docs.python.org/3.8/library/concurrent.futures.html
Multi-process code can, of course, address some of these issues, but it comes at the cost of spinning up multiple OS processes and costly inter-process communication etc.
It's a confusion that has been quickly resolved every time a team mate of mine has run into the issue, but I have seen it happen multiple times (at different places).
For one, regardless of the GIL it is still possible to have races in multithreaded python code. You have to worry about RMW because the threads are arbitrarily preemptible at a bytecode boundary. This is not the case with asyncio.
Additionally you can release the GIL when making calls to C code. Asyncio is giving you concurrency by using nonblocking calls, you can’t run two CPU bound threads in parallel with it. But you can do this with python threads. So it’s not true that neither can accomplish more than the other.
[1] https://docs.python.org/3.8/library/concurrent.futures.html#...
The article didn't limit its focus to I/O, though. It felt more like an explanation of modern concurrency and asynchronous options in Python (besides just threading and and multiprocessing).
The only practical issue with the GIL in Python is that it forces you to use new (heavy) Python processes to parallelize computationally heavy tasks. Not so much of a concern depending on your application, but it is a gotcha for people new to Python.
The real issue, though, as your parent poster mentioned, is the number of different ways to access asynchronicity and concurrency in modern Pythons. It really does run counter to Python PEP20 [0] and can lead to some communication difficulties among developers.