Specifying the order of execution for your goroutines

Although you should not make any assumptions about the order in which your goroutines will be executed, there are times when you need to be able to control this order. This subsection illustrates such a technique using signal channels.

You might ask why choose to execute goroutines in a given order when simple functions could do the same job much more easily. The answer is simple: goroutines are able to operate concurrently and wait for other goroutines to end, whereas functions executed in sequence cannot do that!

The name of the Go program for this topic is defineOrder.go, and it will be presented in five parts.

The first part of defineOrder.go follows next:

package main 
 
import ( 
    "fmt" 
    "time" 
) 
 
func A(a, b chan struct{}) { 
    <-a 
    fmt.Println("A()!") 
    time.Sleep(time.Second) 
    close(b) 
} 

The A() function is blocked by the channel stored in the a parameter. Once that channel is unblocked in the main() function, the A() function will start working. Finally, it will close the b channel, which will unblock another function, in this case function B().

The second code portion of defineOrder.go is shown in the following Go code:

func B(a, b chan struct{}) { 
    <-a 
    fmt.Println("B()!") 
    close(b) 
} 

The logic in B() is the same as in the A() function. The function is blocked until the a channel is closed. Then it does its job and closes the b channel. Note that the a and b channels refer to the names of the parameters of the function.

The third code segment of defineOrder.go follows next:

func C(a chan struct{}) { 
    <-a 
    fmt.Println("C()!") 
} 

The C() function is blocked and waits for the a channel to close in order to start working.

The fourth part of defineOrder.go contains the following code:

func main() { 
    x := make(chan struct{}) 
    y := make(chan struct{}) 
    z := make(chan struct{}) 

These three channels will be the parameters for the three functions.

The last code segment of defineOrder.go contains the following Go code:

    go C(z) 
    go A(x, y) 
    go C(z) 
    go B(y, z) 
    go C(z) 
 
    close(x) 
    time.Sleep(3 * time.Second) 
} 

Executing defineOrder.go will generate the desired output even though the C() function was called multiple times:

$ go run defineOrder.go
A()!
B()!
C()!
C()!
C()!

Calling the C() function multiple times as goroutines will work just fine because C() does not close any channels. However, if you call A() or B() more than once, you will most likely get an error message such as the following:

$ go run defineOrder.go
A()!
A()!
B()!
C()!
C()!
C()!
panic: close of closed channel
goroutine 7 [running]:
main.A(0xc420072060, 0xc4200720c0)
      /Users/mtsouk/Desktop/defineOrder.go:12 +0x9d
created by main.main
      /Users/mtsouk/Desktop/defineOrder.go:33 +0xfa
exit status 

As you can see from the output, the A() function was called two times. However, as the A() function closes a channel, one of its goroutines will find that channel already closed and generate a panic situation. You will get a similar panic situation if you call B() more than once.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.214.155