bugs caused by these issues are difficult to find using testing debugging them is also difficult because they may not be reproducible since they are usually load dependent it is also often true that it is not possible to realize the performance implied by parallelism--sometimes critical task cannot be parallelizedmaking it impossible to improve performanceregardless of the number of processors added similarlythe overhead of communicating intermediate results between processors can exceed the performance benefits parallel computing boot camp semaphore is very powerful synchronization construct conceptuallya semaphore maintains set of permits thread calling acquire(on semaphore waitsif necessaryuntil permit is availableand then takes it thread calling release(on semaphore adds permit and notifies threads waiting on that semaphorepotentially releasing blocking acquirer import threading class semaphore ()def __init__ (self max_available )self cv threading condition (self max_available max_available self taken def acquire (self)self cv acquire (while (self taken =self max_available )self cv wait (self taken + self cv release (def release (self)self cv acquire (self taken - self cv notify (self cv release (start with an algorithm that locks aggressively and is easily seen to be correct then add back concurrencywhile ensuring the critical parts are locked when analyzing parallel codeassume worst-case thread scheduler in particularit may choose to schedule the same thread repeatedlyit may alternate between two threadsit may starve threadetc try to work at higher level of abstraction in particularknow the concurrency libraries-don' implement your own semaphoresthread poolsdeferred executionetc (you should know how these features are implementedand implement them if asked to table top tips for concurrency