The type of development I grew up learning in the 80's was serial. Data entered into a program through files or standard input. Functions then transformed data through a series of discrete intermediate steps, generating outputs as they ran, or directly funneling generated data into other functions. Final outputs were stored in human readable files, formats for other programs, or a combination of both.
The advantages of this pattern include simplicity of state, ease of tracking down bugs, and the ability to reroute functional flow at any stage of processing. Unfortunately serial programming is limited by bottlenecking I/O, and fails to utilize multicore or large network resources.
When I began working as an engineer in the mid 90's I suddenly had access to more computational power. An easy way to scale programs is to run the code across several processors. This is commonly referred to as horizontal scaling, and is a straightforward way to scale serial programs.
Care must be taken to duplicate intermediate data to separate systems over networks. Local multiprocess implementation is limited by bus bandwidth to and from disks to memory, memory to and from processors, as well as computational complexity.
In the last decade I was introduced to a form of concurrency that maintains shared memory space and initial state, but with individual stacks. Multithreading ameliorates the need to wait on bottlenecks from disk to memory for multi process programs. Program state is managed for multiple threads by a series of semaphores which broker conditional access to limited resources, the simplest being a Mutex or lock. Atomic operations are a simple form of synchronization and safe to be called by threads at any time.
Web programming and large network development delivers access to dedicated systems. Each call yields advanced processing by leveraging remote resources. With a growing reliance on networks of systems, asynchronous callbacks or non-blocking I/O enables processors to avoid waiting and continually reacts, much like graphical user interfaces. The reactor pattern is an extremely popular implementation of asynchronous processing for web design. Events can be processed without requiring event handlers to block program flow.
The above concurrent patterns may be combined, bringing serial, multi threaded, multi process and asynchronous modules together into a single application. Each concurrent pattern resolves a class of execution needs. Practical experience serves as the basis for selecting the proper pattern to meet a given processing requirement.