Simple, Simon and Sage with Gophers.
By Gemini 5 Oct, 2025.

Chat about Goroutines

by: burt rosenberg
at: university of miami
date: october 2025

Overview

This is the result of a conversation I had with ChatGPT, in response to the class presentation about the concurrency examples given in this github repository.

Class discussion indicated that,

  1. A little bit more time was needed to introduce the Go programming language. All is not so obvious.
  2. The nature of goroutines and channels should be built up carefully, before applying it to the case of accumulate.
I hope this dialogue addresses these issues.

Cast of characters

Simon
The knowledgeable programmer. Confident and clear, Simon explains concepts in an abstract manner.
Sage
The thoughtful and curious observer. Skeptical at times and not afraid to challenge Simon, Sage voices questions and frustrations that many learners feel.
Simple
The beginner student. Curious, eager to learn, and often reacts to explanations, making the dialogue accessible for readers who are new to computer science.

Introduction to Go

Simple:In class an example was giving of concurrency when programing the Go programming language. But I don't know Go.
Sage: Yes Simple, I also do not know Go. Maybe a simpler example go program with explanations will help.
Simon: Oh OK. Let us look at this example Go program. Simple, what do you think this program does?
Simple: Well I am pretty sure func introduces a function, like def does in Python. And I am pretty sure that Go uses curly braces like C to make blocks of code. The braces after func probably enclose the function body. And maybe like C language, the func main is the starting point to run the code.
Simon: Sage, what do you see in this program?
Sage: Well it looks like Go does not declare types as C does. The (s string) after func f means that Go has a string type, and declaration of variables have type name follow the variable name. But I see both = and := that look like assignment?
Simon: Yes the short variable declaration statement of the form
      variable := value
  
both assigns the value and defines the variable, inferring the type of the variable from the type of the value.
Simple: Well if there is a short variable declaration, is there a not-short one?

Simon: Yes, there are many not-short forms, beginning with the keyword var and using the simple equals sign. Here is a simple example
      var i, j, k int
  
Sage: And so once initialized, the variable value is updated with the equals sign like in C?
Simon: Mostly. Once declared with :=, all updates use =. But the gramatical details are different. In Go there is an assignment statement,
     ExpressionList [ add_op | mul_op ] = ExpressionList
for instance, =, += or <<=. In C, equals is an operator, it can go anywhere in an expression.
Sage: The for keyword looks like it is working as a while in C?
Simon: In Go the for statement is defined as,
       for [ Condition | ForClause | RangeClause ] Block
   
The version in this program uses Condition. The ForClause version would look like the C initialization; condition; increment pattern, except no parenthesis. Or the ForClause can be empty and it is a do-forever loop.

Goroutines

Simple: What does this line do,
     go f(hello[i])
  
Simon: Oh that creates and starts a goroutine. In Go, a goroutine is a lightweight thread of execution managed by the Go runtime.

It is concurrent, not necessarily parallel. Multiple goroutines can run independently, and the Go scheduler multiplexes them onto OS threads.

Sage: Is there a reason Go created goroutines, rather than using a library like pthreads?
Simon: Several reasons,
Simple: Well what about all that concurrency stuff I learned, like mutex's and monitors and PV-semaphores.
Simon: Well I am not saying that all of that stuff goes out the window, but Go follows another school of thought about concurrency.
Goroutines synchronize primarily via channels, a typed conduit for messages between goroutines.
Also, goroutines have an immediate, non-blocking start: the go statement continues immediately; execution of the function runs concurrently.
Sage: So now that explains the output of hello.go.
 * % make hello
 * go run hello.go
 * ollhe
 * % make hello
 * go run hello.go
 * ohlel
A bunch of goroutines are started, each with a letter to print. They are concurrent so at one point or another they print the letter. But there is no synchronization between the prints.

The Rendez-Vous

Simple: Will this explain the accumulate.go program shown in class?
Simon: Sure it can, but let us look at another program, for another synchronization requirement first. Let us think about the rendez-vous.

Suppose Alice and Bob have tasks A1 and A2 for Alice, and B1 and B2 for Bob. Alice will do A1 then A2, and Bob will do B1 and B2. As Alice and Bob are by nature concurrent, we have not ordering of either A1 or A2 to either of B1 or B2.

Suppose in addition to wanting A1 to come before A2, we also demand A1 comes before B2. Likewise, we demand B1 come before A2. See the diagram.

We can assure this ordering if we create a meet-up point in time between Alice and Bob, an even R we call a Rendez-Vous event. If we can create such a synchronization primitive then we can solve he problem of time ordering as demanded.

            A1     R  A2
             |     |  |
    A:   ----+-----+--+--------------
                   |
                   | 
    B:   --------+-+-----+-----
                 | |     |
                B1 R     B2
Sage: Let me see if I have this correct. For Alice, events A1 and and A2 are arranged in time as A1 < A2. Likeswise for Bob, B1 < B2. Is it a good idea to use < to express these orderings?
Simon: We can try it. And we can assume that in a thread, every event has a necessary time ordering, determined by the sequential nature of the code.
Simple: This is beginning to get heavy. So A1 and A2 are events, like lines in a program?
 Alice:
     // some code
     A1: x = 2
     // more more code
     A2: y = 3
 
And A1 < A2 because in the program of Alice, the execution of line A1 must occur before the execution of line A2? Those are events, things that happen and we want to order them in time?
Simon: Yes.
Sage: And we also want A1 < B2, and B1 < A1. But it is not so clear how to make that happen, since A and B are concurrent threads, and no such time order relationships are naturally required between threads.
Simon: Right. So we dream up a rendez-vous event R which is shared by Alice and Bob. For alice,
      A1 < R < A2
  
and for Bob,
      B1 < R < B2
  
Simple: Let me try the math. So A1 < R, in Alice's thread. And R < B2 in Bob's thread. But R is the same event, so,
      A1 < R < B2
  
And so we have A1 < B2. Likewise B1 < A2.
Simon: That's the plan. A good plan?
Sage: Good plan. How do we do it.
Simon: Channels. In particular an unbuffered channel. In Go, an unbuffered channel for type integers is created like this,
        ch := make(chan int) 
The channel has two operations. Receive and send.

The receive operator receives a value from the channel. If there is no value in the channel, the operator blocks until this is one. Then the value of expression with the operator is the received value.

     i := 0 // an integer. to match the channel type
     // some code
     i = <- ch
The send statement sends a value of the correct type into the channel, if there is room in the channel.
        ch <- 1
An unbuffered channel never has room. Therefore, sender and receiver will rendez-vous on the event "transfer of a value".
Simple: So the event that above we called R is when an integer value is passed from the Alice goroutine to the Bob goroutine?
Simon: Yes. nd in the code this R is at The send statement in the Alice's goroutine, and the receive operator in Bob's goroutine are the event R. The threads will align their flow at that event.
Sage: So this explains the rendez-vous program pretty well. There are two globals that need to be set. Each of two goroutines sets one. And there they each print the values.

No print should happen until both routines have advanced down their code enough that the globals have been set to the correct values. So they rendez-vous on the event that they have set their global, and then continue after the rendez-vous to the event the printing of the globals.

Simon: That is a good guess that,
      var a_g int = 0
      var b_g int = 0
were globals. As in C they are defined in file scope. But those words do not really apply in Go. They are called package scope.
Sage: Nice to know. I once won in Jeopardy with Computer Nerd for $500 with "What is variable hoisting."

Exercise: Mutex

Simon: You should try to create a mutex out of a channel. The accumulate solution is not really a mutex. Rather it passes a token back and forth to the main thread. It is safe for mutual exclusion; it is live and mostly fair. But a mutex would be self-contained, would not need an entire thread just to be keeper of the token.

Simple: We get a hint?
Simon: Of course. Think of a PV-semaphore initialized to one. How is that like a buffered channel with capacity 1,
      ch := make(chan int, 1)
    
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

author: burton rosenberg
created: 5 oct 2025
update: 10 oct 2025