IN 1995, WHEN I WAS IN MY SECOND YEAR IN COLLEGE, I was introduced to UNIX network programming. In C, you could create sockets to open TCP connections to servers and code the servers that accepted these connections. I remember the excitement I felt the first time I created a TCP server: I could accept connections and receive and send messages on them.
If I wanted my server to accept many concurrent connections, the common solution was to use threads, and soon I had created my first multi-threaded TCP server. This server accessed a shared data structure, which needed to synchronize the access to all the client threads that had been spawned. Getting the synchronization fine-grained (to maximize resources and time) and right (to avoid deadlocks) proved to be more difficult than anticipated.