I due problemi più comuni nella mia esperienza di prendere una lezione sull'argomento sono stati il debug dei programmi e l'efficiente distribuzione delle risorse.
Il debug di un programma parallelo, in particolare su un sistema di thread gestito in modo indipendente, è sorprendentemente difficile. Non è deterministico, il che significa che il tuo programma può funzionare 999 volte su 1000 e che una volta fallisce solo perché qualcosa è arrivato nell'ordine sbagliato o il gestore thread non ha assegnato correttamente.
Anche gli errori di atomicità sono comuni, ecco perché i linguaggi funzionali stanno diventando popolari per gestire la concorrenza mentre stanno fuori dallo stato (non è che non abbiano uno stato, sia trasparente).
L'altro grande problema è la comunicazione. Questo rientra in due categorie: risorse e tempo. Resourcing fa riferimento a dimensioni della cache e allocazioni di memoria spesso limitate per singoli processi. I sistemi paralleli spesso vedono l'uso in applicazioni pesanti di dati, quindi il flusso e il confezionamento corretti di tali dati sono importanti. C'è anche il problema di affrontare il fatto che potrebbe non esserci un buon algoritmo che fa in modo efficiente quello che vuoi. Alcune attività non sono facilmente parallelizzabili e, anche se lo sono, la comunicazione in questione può rendere discutibile qualsiasi guadagno di velocità. Il tempo si riferisce ai tempi di comunicazione tra processori. Questo è meno un problema nei sistemi "multicore" rispetto a quelli distribuiti, ma è ancora un problema.
Potresti chiedere perché sto parlando di sistemi distribuiti. Bene, alcuni sistemi di "memoria condivisa" sono effettivamente distribuiti con particolari funzionalità che li fanno funzionare come memoria condivisa.