CategoryFunctions in Go

Concurrency and Goroutines in Go

In this post, we will be looking at goroutines in Go. Go is a language that was built with concurrency in mind. Given that almost all of the computers of the modern era employ the use of multiple cpu cores, this is a natural approach. Go makes use of a special feature called the Goroutine to handle concurrency. You can use these goroutines to call on lightweight threads to run a function. Before we take a look at examples, lets talk about concurrency for a moment.

Concurrency in Computing

Concurrency is the use of independent processes that work in an asynchronous order to complete a task. There are many examples of concurrency in the real world. We can look at how trains share tracks with one another, how in multiplayer games each player can make an action independent of one another, and how society in general works with each person making their own decisions. Naturally, many things use a model of concurrency.

Logically, it makes sense to apply this type of behavior to a computer program. When you download a file, listen to an audio stream, send a message over the web, print out a document, or even type in a text editor, you are making use of concurrency in programs. Some programming languages use different methods to handle concurrency and Go is certainly no exception with it’s goroutines. 

go-routines-gopher

Examples of Goroutines in Go

The simple definition of a goroutine, is a function that is capable of running concurrently with other functions. To invoke a goroutine, we simply place the word go in front of a function execution call. Let us look at a very simple example of this.

In this example we use a for-loop to iterate through ten values (0 to 9) inside of our function. Our main function has its own thread and in a way, the main function itself runs like a goroutine. We invoke a second goroutine with our function by typing go a(0). If we were to remove the goroutine and the Scanln function call, the function would run normally and then the program would end after execution. However, because the function is running on a separate thread then the main function, if we were to remove the Scanln call and leave the goroutine, the program will terminate before the end of the function. This happens because the main function doesn’t wait for the goroutine to end before moving on to execute the rest of the code.gopher_pipe

Because goroutines are very lightweight threads, we can literally call on thousands of them in a piece of code (the average 64 bit cpu can use 100,000 goroutines at once). Let us look at an example that uses many goroutines.

In this example, we are assigning ten threads (labeled 0 to 9) to our function by using our for loop. In our function, we are iterating through ten values again, however, each time we are now stopping and waiting for a random period of time which is between 0 and 250 milliseconds before the thread continues execution. In this way, we can see the way the threads are actually running parallel and concurrent to one another. You will notice that threads that were deployed before others may end execution well after the others have finished.

Conclusion

In this post we looked at how we can spawn threads and run functions concurrently in Go. The talked about goroutines and gave a general overview of concurrency. In our next few posts we will talk about how we can send data from one goroutine to the other using channels. 

Functional Recursion in Go

In the last post, we talked about some of the basic function types and features in Go. In this post, we are going to discuss recursion in Go and recursion in general. Recursion can be a confusing concept at first. Fortunately, recursion in Go is very straight forward. 

Recursive Functions

recursive-function

A recursive function is a function that makes a call to itself inside of itself. Recursion is very strong for certain types of algorithms. Recursive functions can be much more efficient then loops in some cases. Let’s look at an example of recursion with Go.

In this simple program, we have a recursive function which will print out an integer decremented by one until it is less than zero. When the value is less than zero, the function just breaks out of the recursion. To do this, we use an empty return statement. We can rewrite this program with a for-loop.

In this case, our programs will have identical behavior and there will be no real difference in performance at this scale. Even if we call an argument of one hundred thousand or one million, we won’t really see any differences. Ultimately, loops and recursive functions each have their pros and cons depending on the implementation. 

Thinking Recursively

The main reason why people use recursion instead of loops is to avoid destructive state changes. Notice that when we use the loop, we have to change the variable for each iteration of the loop. In our recursive example, the original value of the variable does not change because each function call creates a new one. In more complex programs, it can be beneficial to avoid destructive state changes by using recursion.

One of the more famous cases of recursion is the Fibonacci series algorithm. We can use recursive function calls to represent this infinite series.

fibonacci-recursion

In this function example, we have three different return statements. If we break down how this function works, we can see a bit more how to think recursively. Because of the if statements, if we put one or zero into the function we will get those same numbers back. The recursion takes place with numbers greater than one. If we put two into our function, we call the function again for fib(2-1) + fib(2-2). This is just like calling zero and one and then adding those two together to get one. If we put three into the function, we will call fib(3-1) + fib(3-2). We can simplify this down to fib(1) + fib(0) + fib(1). We can keep doing this with ever higher numbers.

As you can see, our Fibonacci series function is just a combination of ones and zeroes. The return we get is the value at that term of the Fibonacci series. For fib(6) we get a return of eight for example because eight is the sixth term in the series. 

Conclusion

In this post, we took a look at recursion in Go. We talked about what recursion is and how we can think recursively. Our next few posts will continue to look at the different styles of functions in Go. 

Using Functions in Go

Functions are a feature in many different languages. Functions allow us to save pieces of code to reuse at any point in our program. This makes things significantly easier for many tasks. Not only does it decrease the amount that we have to write, it also makes the code more readable. Functions in Go have many specific features that make them particularly useful.

Basic Function Styles in Go

Go supports many different styles of functions. In Go, you can create first-class functions, custom function types, function literals, and functions with multiple return values. You may also use functions as a closure and define high-order functions. We will talk about each of these types of functions one at a time as we work through this tutorial. First, let’s just look at a basic function in Go and how it is structured.

functions in goHere are two simple examples of functions in Go. Our first example just calls the fmt.Println() function inside of it which prints the string “Hello, World” then creates a new line. It takes no arguments and we can simply call it by typing its name followed by a set of parenthesis. Our addition function, allows us to pass two integer values to it. It combines these two values with the addition operator and returns that sum as an integer. 

Notice that we define the types in this function. This serves two main purposes; It allows us to make the function more readable and it makes the function type safe. In this case, our function can only accept integer values and will only return integer values. This removes any unpredictable behavior from the function. Also, we can make this function definition shorter because of its successive type information. Look at the third example for this.

Return Statements in Go and Variadic Functions

We have an explicit return statement in this function. In Go, functions won’t automatically return the last line; we must use the return operator. In this case, we return the sum. We can also use this return operator to return multiple return statements from a function. Let’s look at an example of this.

Go also can make use it’s slice abstraction when we want to pass a non-set amount of arguments to a function. This is what’s called a variadic function. Most of the core functions in Go are variadic, which means that you can pass any number of arguments to them. If we wanted to make a summation function we would use a variadic function to do this.

In this example, we use this triple dot syntax to signify that we can take an arbitrary amount of arguments. It also tells the compiler to make a slice out of this data. For our first execution of sum, we pass nine arguments to it. The function then treats these like a slice of integers. We use the range keyword to iterate through all of the values and we add them all together. We then return a single integer. 

Conclusion

In this post, we took a look at basic functions in Go. We looked at how we can use functions with no arguments or returns, functions that have arguments and/or returns and functions with multiple returns. We also looked at variadic functions in Go. In our next few posts, we will look at recursion and some of the other types of functions in Go.