Go Concurrency versus C and pthreads

Apples and Oranges?

I recently participated in the following programming challenge:

Write a program to test user reflexes: when it runs, it waits a random amount of time between 1 and 10 seconds, and prints "GO!" as a prompt.

It then expects the user to hit the enter key, and times how fast enter is pressed, in milliseconds.

If the enter key is pressed before the "GO!" prompt appears, the program prints "FAIL".

It's essentially an exercise in concurrency: have one piece of logic listen to the keyboard, for the key press, while simultaneously generating a random number for the sleep duration, and finally comparing the event timestamps.

Since I have been learning Go, the "GO!" prompt in the challenge description led me to think of using goroutines and channels to solve it.

As it turns out, doing it this way is pretty easy:

Then I started thinking about how Go was influenced by C, or the idea that "Go is a modern C", and wondered how difficult would it be to reproduce the same program in plain C?

There are no references to threads in the original ANSI C specification nor even in the second edition of K & R, but POSIX threads, or pthreads, have come along since then, as an optional library.

While it's a bit more verbose, conceptually it is very similar:

Listening for key press events is essentially the same, and gettimeofday provides nice time capture and compare functions, similar to Go's time package.

Creating a pthread is relatively straight-forward, but instead of using a channel to communicate a result, I had to pass a struct by reference.

The other big difference is that the created pthread has to be joined, to wait on its termination.

This example, with its detailed explanations, was invaluable for wrapping my mind around how pthreads work.

Both the create and join event are fragile, though, and need to be monitored for errors. In this toy program, I just exit if either of those things happen, but it would cause real complexity for a more serious application.

Programming logic aside, there is another distinction with what's happening under the hood.

While pthreads are lighter-weight than full blown operating system processes, they are still much more resource intensive than goroutines. Go's concurrency is based on the Communicating Sequential Processes (CSP) model.

As a result, its goroutines are multiplexed to a small number of operating system threads, instead of using one thread for each goroutine.

But perhaps this is an unfair comparison: C is much older than Go, and pthreads were a later add-on.

Go, in contrast, had the benefit of C's experience, alongside other developments in CS theory and practice.