Once you start using tasks (or threads as we have seen) you'll probably be facing the need for synchronization. The ability to spread code execution simultaneously across multiple cores does not eliminate the occasional need for some kind of synchronization. You may parallelize some code execution but, subsequently, you may need to wait for some of the tasks to be accomplished before proceeding further. Or, as already said, you may need to find some mechanism to properly access some shared resources that would get ruined if accessed indiscriminately.
A number of functionalities to deal with tasks are built into the PPL. Let's have a brief overview of the most significant ones.