Good concurrency requires a lot more than throwing a few threads in an application and hoping for the best. There's a range in how concurrent a program can be going from embarrassingly parallel to pure sequential. Any given program can use Amdahl's law to express how scalable a problem or algorithm is. A couple qualifications for a embarrassingly parallel application would be:
- No shared state, every function only depends on the parameters passed in
- No access to physical devices (graphic cards, hard drives, etc)
There are other qualifications, but with just these two we can understand why games in particular are not as easy as you might think to take advantage of multiple cores. For one, the model of the world that will be rendered has to be shared as different functions calculate physics, movement, apply artificial intelligence etc. Second, each frame of this game model has to be rendered on screen with a graphics card.
To be fair, many game makers use game engines that are produced by third parties. It took a while, but these third party game engines are now much more parallel than they used to be.
There are bigger architectural challenges in dealing with effective concurrency
Concurrency can take many forms, from running tasks in the background to a full architectural support for concurrency. Some languages give you very powerful concurrency features such as ERLANG, but it requires you to think very differently about how you construct your application.
Not every program really needs the complexity of full multicore support. One such example is tax software, or any form driven application. When most of your time is spent waiting on the user to do something, the complexity of multithreaded applications are just not that useful.
Some applications lend themselves to a more embarrassingly parallel solution, such as web applications. In this case, the platform starts out embarrassingly parallel and it's up to you not have to impose thread contention.
The bottom line:
Not all applications are really hurt by not taking advantage of multiple threads (and thus, cores). For the ones that are hurt by that, sometimes the computations are not friendly to parallel processing or the overhead to coordinate it would make the application more fragile. Unfortunately, parallel processing is still not as easy as it should be to do well.