Go, also known as Golang, is a modern programming language developed by Google in 2009. It is a compiled, statically typed language that is designed to be simple, reliable, and efficient. Go is used to build a wide range of software applications, including web servers, distributed systems, and command-line tools.
Advantages
Simple: Go is a simple language to learn and use. It has a small syntax and a small standard library. This makes it easy to get started with Go and to write code that is easy to read and maintain.
Reliable: Go is a reliable language. It has several features that make it less prone to errors, such as garbage collection and type safety.
Efficient: Go is an efficient language. It compiles to native machine code and has several features that make its code fast, such as goroutines and channels.
Scalable: Go is a scalable language. It is designed to support large-scale distributed systems.
Modern: Go is a modern language. It has several features that make it well-suited for modern software development, such as concurrency support and generics.
The Hello World
Below is the code for writing Hello world in golang.
package main
import "fmt"
func main(){
fmt.Println("Hello World");
}
//output
//Hello world
Let's break down the code
package main
The package in Go which we write in every Go file is the package
declaration. This declaration is the first line of code in a Go file and it tells the Go compiler which package the file belongs to.
Packages are a way to organize Go code into modules. Each Go file belongs to a package and all of the code in a package is compiled together. Packages can be imported into other Go files using the import
statement.
The package
declaration must be the first line of code in a Go file and it must be followed by the name of the package. The package name must be unique and it cannot be the same as the name of any other package on your system.
import "fmt"
The fmt package provides Println,Printf,Print
Here we are using Println
to print contents to the console.
Inside Println
we can pass string/var/const . whatever the content we want to print to the console.
Variables
package main
import "fmt"
//making the first letter capital makes the variable or constant public
const LoginToken string="vsusuwsnuwshwns"
func main(){
var username string="Vishnu"
fmt.Println(username)
fmt.Printf("Variable is of type: %T \n",username)
var value uint8=56
fmt.Println(value)
fmt.Printf("Variable is of type: %T\n",value)
var isTrue=false
fmt.Println(isTrue);
fmt.Printf("Variable is of type: %T\n",isTrue)
var smallFloat float32=56.937378383
fmt.Println(smallFloat)
fmt.Printf("Variable is of type: %T\n",smallFloat)
var anotherVariable int
fmt.Println(anotherVariable)
fmt.Printf("Variable is of type: %T\n",anotherVariable)
var sampleString string
fmt.Printf(sampleString)
fmt.Printf("Variable is of type: %T\n",sampleString)
//implicit types
var name="Tommy"
fmt.Println(name)
//no var style
numberOfUser :=300000
fmt.Println(numberOfUser);
fmt.Println(LoginToken);
fmt.Printf("Variable is of type: %T\n",LoginToken)
}
In the above code, we can see %T T
his is a format verb which is used to print type of the data. The same as %v
format verb is used to get data itself.
In Golang there are different types of data we can use to store data. Below are the data types
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
UserInput
package main
import (
"bufio"
"fmt"
"os"
)
func main(){
welcome:="Welcome to user input"
fmt.Println(welcome)
reader:=bufio.NewReader(os.Stdin)
fmt.Println("Enter the rating for our Pizza:");
//comma ok syntax || error ok syntax
input,err:=reader.ReadString('\n')
fmt.Println("Thanks for rating",input)
fmt.Printf("Type of rating %T",input)
}
In the above example, we can see :=
operator. It is used for short declaration and assignment of the variable. Where as =
is used as a normal assignment variable
In the above example, we are using two packages os
and bufio
to read the user input from the console.
Here we are creating a newreader with help of bufio which takes inputs from os.Stdin which allows reading inputs from the keyboard
Here we will introduce the try catch of golang called comma ok syntax
input,err:=reader.ReadString('\n')
Here the left function may return the output or error. So we are using two variables to handle both output and err.
In the input
variable we will get the user input which we can print to the console.
Conversions
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main(){
fmt.Println("Welcome to Pizza app")
fmt.Println("Please rate our Pizza between 1 and 5")
reader:=bufio.NewReader(os.Stdin)
input,_:=reader.ReadString('\n')
fmt.Println("Thanks for rating",input)
numRating,err:=strconv.ParseFloat(strings.TrimSpace(input),64)
if err !=nil{
fmt.Println(err)
}else{
fmt.Println("Added 1 to your rating:" ,numRating+1)
}
}
In the above example, we are converting userinput (string) to float. As the userinput always be string, we are trying to convert it to float,int or any as required.
We are using strconv
to convert the string to float and strings.TrimSpace
to trim the white spaces in userinput
Random Number
package main
import (
"fmt"
"math/big"
//"math/rand"
"crypto/rand"
)
func main(){
fmt.Println("Welcome to maths in golang")
//var mynumberOne int=2
//var mynumberTwo float64=4
// fmt.Println("The sum is :", mynumberOne+int(mynumberTwo))
//random number
//fmt.Println(rand.Intn(5))
}
In the above example, we are using rand
from math
With math
: we are using Intn(n)
which will return us a random number till n
Time
package main
import (
"fmt"
"time"
)
func main(){
fmt.Println("Welcome to time in golang")
presentTime:=time.Now()
fmt.Println(presentTime);
fmt.Println(presentTime.Format("01-02-2006 15:04:05 Monday"))
createdDate:=time.Date(2020,time.December,10,23,23,0,0,time.UTC)
fmt.Println(createdDate)
fmt.Println(createdDate.Format("01-02-2006 Monday"))
}
In the above example, we have used the time package to get present time from our system. There are different formatting for print time in Golang.
"01-02-2006 15:04:05 Monday"
This is the ideal time should use in Golang to format time and date.
We can create our own timestamp with time.Date()
function
Pointers
A pointer in Go is a variable that stores the memory address of another variable. Pointers are useful for passing references to values and records within your program.
To declare a pointer, you use the *
operator followed by the type of the value that the pointer will point to. For example, the following code declares a pointer to an integer:
var p *int
To assign a value to a pointer, you use the &
operator followed by the variable that you want to point to. For example, the following code assigns the address of the variable x
to the pointer p
:
x := 10
p = &x
Once you have a pointer to a value, you can use the *
operator to dereference the pointer and access the value that it points to. For example, the following code prints the value of the variable x
to the console:
fmt.Println(*p)
//output
//10
Pointers can also be used to modify the values of variables. For example, the following code increments the value of the variable x
by 1:
*p++
//above expression is equivalent to below one
x++
Pointers can be useful in a variety of situations. For example, pointers can be used to:
Pass references to values to functions.
Return references to values from functions.
Create linked lists and other data structures.
Implement efficient algorithms.
However, it is important to use pointers carefully, as they can be dangerous if used incorrectly. For example, if you dereference a nil pointer, your program will crash.
Here is an example of how to use pointers in a function:
func increment(p *int) {
*p++
}
func main() {
x := 10
increment(&x)
fmt.Println(x)
}
//output
//11
In this example, the increment()
function takes a pointer to an integer as its argument. The function then dereferences the pointer and increments the value that it points to.
Pointers can be a powerful tool, but it is important to use them carefully. If you are new to Go, I recommend reading the documentation on pointers before using them in your code.
Arrays
In golang we are not going to use arrays much we delegating all the usual functionalities of arrays to slices in golang
package main
import "fmt"
func main(){
fmt.Println("Welcome to array in golangs")
var fruitList [5]string
fruitList[0]="Apple"
fruitList[1]="Orange"
fruitList[2]="Banana"
fruitList[3]="Strawberry"
fruitList[4]="Pineapple"
fmt.Println("Fruit List is ",fruitList)
fmt.Println("Fruit List is ",len(fruitList))
var vegList =[5]string{"tomato","potato","beans"}
fmt.Println("Veg list",vegList)
}
len()
is used to get the length of the array
In the above example we have used array index to assign values to and there is other syntax to assign while declaration in vegList
example
Slices
package main
import (
"fmt"
"sort"
)
func main() {
fmt.Println("This is on slices")
var fruitList = []string{"Apple", "Banana", "Peach"}
fmt.Printf("Type of fruitList %T\n", fruitList)
fruitList = append(fruitList, "Mango", "Tomato")
fmt.Println(fruitList)
fruitList = append(fruitList[1:])
fmt.Println(fruitList)
highScores := make([]int, 4)
highScores[0] = 244
highScores[1] = 944
highScores[2] = 744
highScores[3] = 544
//highScores[4]=000 this doesnt work
highScores = append(highScores, 666, 888, 999)
fmt.Println(highScores)
sort.Ints(highScores)
fmt.Println(highScores)
fmt.Println(sort.IntsAreSorted(highScores)) // returns boolean
//remove a value from slice
var courses = []string{"react", "javascript", "swift", "python"}
fmt.Println(courses)
var index int = 2
courses = append(courses[:index], courses[index+1:]...)
fmt.Println(courses)
}
In the above example we are using slices.
var fruitList = []string{"Apple", "Banana", "Peach"} T
his is how we declare slices in golang and assign values to it.
For making slices we can use memory allocation functions like make to create fixed memory storage to store slice values as shown in the above example
But we can use append
more values to a slice even if there is confined memory allocated to a slice.
courses = append(courses[:index], courses[index+1:]...)
This is used to delete a value in a slice at a specific index.
Memory allocation
The new()
and make()
keywords in Go are used to allocate memory and create new values. However, they work differently and are used for different purposes.
new()
The new()
keyword allocates memory for a new value of the specified type and returns a pointer to that value. The type of the value must be specified when the new()
keyword is used. For example, the following code allocates memory for a new integer value and returns a pointer to that value:
p := new(int)
The new()
keyword can be used to allocate memory for any type of value, including arrays, slices, maps, and structs.
make()
The make()
keyword is used to create new slices, maps, and channels(we will discuss). The type of the slice, map, or channel must be specified when the make()
keyword is used. For example, the following code creates a new slice of integers:
s := make([]int, 0)
The make()
keyword can also be used to create a new slice or map with a specific capacity. For example, the following code creates a new slice of integers with a capacity of 10 elements:
s := make([]int,0, 10)
The line of code s := make([]int, 0, 10)
declares a new slice of integers with a capacity of 10 elements.
The make()
function is used to create new slices, maps, and channels. The first argument to the make()
function is the type of the slice, map, or channel to be created. In this case, the first argument is []int
, which means that we are creating a slice of integers.
The second argument to the make()
function is the initial length of the slice, map, or channel. In this case, the second argument is 0
, which means that the slice will be initially empty.
The third argument to the make()
function is the capacity of the slice, map, or channel. The capacity is the maximum number of elements that the slice, map, or channel can hold without having to be reallocated. In this case, the third argument is 10
, which means that the slice can hold up to 10 elements without having to be reallocated.
When to use new() and make()
The new()
keyword should be used to allocate memory for new values of any type, including arrays, slices, maps, and structs. The make()
keyword should be used to create new slices, maps, and channels.
Here is a table that summarizes the difference between new()
and make()
:
Operator | Usage |
new() | Allocates memory for a new value of the specified type and returns a pointer to that value. |
make() | Creates a new slice, map, or channel and allocates memory to it |
Array vs Slice
The main difference between a slice and an array in Go is that a slice is a reference to a contiguous segment of an array, while an array is a fixed-size data structure.
Arrays
Arrays are fixed-size data structures that store a series of values of the same type. The size of an array is specified when it is declared. For example, the following code declares an array of 10 integers:
Go
var arr [10]int
Once an array is declared, its size cannot be changed. To add or remove elements from an array, you must copy the array to a new array of a different size.
Slices
Slices are references to contiguous segments of arrays. Slices can be of any size, up to the size of the underlying array. To create a slice, you use the [:]
operator followed by the array that you want to slice. For example, the following code creates a slice of the first 5 elements of the array arr
:
Go
s := arr[:5]
Slices can be resized using the append()
function. For example, the following code appends the value 11
to the end of the slice s
:
Go
s = append(s, 11)
Slices can also be copied using the copy()
function. For example, the following code copies the slice s
to the slice t
:
Go
t := make([]int, len(s))
copy(t, s)
When to use slices and arrays
Slices are generally preferred over arrays because they are more flexible and dynamic. Slices can be resized and copied easily, and they can also be passed to functions as arguments.
Arrays are typically used when you need to store a fixed-size amount of data. For example, you might use an array to store the coordinates of a pixel on a screen.
Here is an example of how to use slices and arrays in a function:
Go
func printSlice(s []int) {
for _, v := range s {
fmt.Println(v)
}
}
func main() {
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s := arr[:5]
printSlice(s)
}
Output:
1
2
3
4
5
In this example, the printSlice()
function takes a slice of integers as its argument and prints the elements of the slice to the console.
The main()
function declares an array of 10 integers and creates a slice of the first 5 elements of the array. The printSlice()
function is then called with the slice as its argument.
Slices and arrays are both powerful data structures in Go. It is important to understand the difference between them so that you can choose the right data structure for your needs.
Maps
package main
import "fmt"
func main() {
fmt.Println("Maps in golang")
languagues := make(map[string]string)
languagues["JS"] = "javascript"
languagues["RB"] = "Ruby"
languagues["PY"] = "Python"
fmt.Println("List of all languages", languagues)
fmt.Println("JS shorts for", languagues["JS"])
delete(languagues, "RB")
fmt.Println("List of all languages", languagues)
//loops
for key, value := range languagues {
fmt.Printf("For key %v,value is %v\n", key, value)
}
}
The maps are key-value pairs.
languages:=make(map[string]string)
This is how we declare a map. We can use what ever data type we want in place of string for key-value pairs.
Assign values to maps
languagues["JS"] = "javascript"
This is how we assign values to a map using the keys and values.
Delete a vale from a map
delete(languages, "RB")
We will pass the map and key to the delete function to delete a specific key-value pair from a map.
Struct
package main
import (
"fmt"
)
func main() {
//there is no inheritence in structs in golang
fmt.Println("This is structs in golang")
Tommy := User{"Tommy", "powusgsg@gmail.com", true, 24}
fmt.Println(Tommy)
fmt.Printf("Tommy details are:%+v\n", Tommy)
fmt.Printf("Name is %v and email is %v", Tommy.Name, Tommy.Email)
}
type User struct {
Name string
Email string
status bool
Age int
}
A struct definition in Golang is a way to create a new type that combines multiple fields of different types. Structs are useful for grouping related data together into a single unit.
This is the syntax to declare a struct
type User struct {
Name string
Email string
status bool
Age int
}
We can create new objects from struct and assign values to as below
Tommy := User{"Tommy", "powusgsg@gmail.com", true, 24}
The Object values can be used as shown below
fmt.Printf("Name is %v and email is %v", Tommy.Name, Tommy.Email)
If else
package main
import "fmt"
func main() {
fmt.Println("if else in golang")
loginCount := 10
var result string
if loginCount < 10 {
result = "Regular user"
} else if loginCount > 10 {
result = "Watch out"
} else {
result = "Exactly 10 login count"
}
fmt.Println(result)
if 9%2 == 0 {
fmt.Println("Number is even")
} else {
fmt.Println("Number is odd")
}
if num := 3; num < 10 {
fmt.Println("Number is less than 10")
} else {
fmt.Println("Number is greater than 10")
}
}
The if else in Golang is the same as in other languages but there is a new syntax where we can declare and initialize a variable and check for conditions as shown below
if num := 3; num < 10 {
fmt.Println("Number is less than 10")
} else {
fmt.Println("Number is greater than 10")
}
Switch case
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("Switch and case in golang")
diceNumber := rand.Intn(6) + 1
fmt.Println("Value of dice is ", diceNumber)
switch diceNumber {
case 1:
fmt.Println("Dice value is 1 and you can open")
case 2:
fmt.Println("Dice value is 2 move to spot 2")
case 3:
fmt.Println("Dice value is 3 move to spot 3")
case 4:
fmt.Println("Dice value is 4 move to spot 4")
case 5:
fmt.Println("Dice value is 5 move to spot 5")
case 6:
fmt.Println("Dice value is 6 roll again!")
default:
fmt.Println("what is what!")
}
}
In the above example, we have created a dice game using a switch case and rand function.
Switch in Golang is the same as in other languages where we have a variable according to the variable value we will switch the condition and execute.
Loops
package main
import "fmt"
func main() {
fmt.Println("Welcome to loops in golang")
day := []string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}
fmt.Println(day)
// for d := 0; d < len(day); d++ {
// fmt.Println(day[d])
// }
// for i := range day { //in i the index is stored not values
// fmt.Println(day[i])
// }
for index, day := range day {
fmt.Printf("index is %v, value is %v\n", index, day)
}
for _, day := range day {
fmt.Printf("value is %v\n", day)
}
rogueValue := 1
for rogueValue < 10 {
if rogueValue == 2 {
goto medo
}
if rogueValue == 5 {
rogueValue++
continue
}
fmt.Println("Value is :", rogueValue)
rogueValue++
}
medo:
fmt.Println("I am in medo")
}
In the above example, we have traditional for-loop syntax along with new syntaxes as discussed below
for index, day := range day {
fmt.Printf("index is %v, value is %v\n", index, day)
}
In the above example index and value are stored and passed to control flow for looping to slice.
for rogueValue < 10 {
if rogueValue == 2 {
goto medo
}
if rogueValue == 5 {
rogueValue++
continue
}
fmt.Println("Value is :", rogueValue)
rogueValue++
}
medo:
fmt.Println("I am in medo")
In the above example, we are using for loop as a while structure where we have only used condition to loop
goto
The goto is used in loops to move the control flow from the loop to execute something else as shown above when the condition is true medo
is executed
Functions
package main
import "fmt"
func main() {
fmt.Println("Welcome to functions in golang")
greeter()
result := adder(3, 5)
fmt.Println("Result is ", result)
proResult, mymessage := proAdder(1, 2, 3, 4, 5, 6, 7)
fmt.Println("ProResult is", proResult, mymessage)
}
func adder(valOne int, valTwo int) int {
return valOne + valTwo
}
func proAdder(values ...int) (int, string) {
total := 0
for _, val := range values {
total += val
}
return total, "hello world"
}
func greeter() {
fmt.Println("Namaste from golang")
}
The function is reusable code which will be called when required.
In Golang we have functions the same as in other languages we need to mention the return type if present and the arguments type.
...int
this dot operator is used to get multiple values from function parameters.
A function can also have two returns as shown below
return total, "hello world"
At this time these returns types must be mentioned in func definition.
Methods
package main
import (
"fmt"
)
func main() {
//there is no inheritence in structs in golang
fmt.Println("This is structs in golang")
vishnu := User{"Vishnu", "vishnuavm5@gmail.com", true, 24}
fmt.Println(vishnu)
fmt.Printf("vishnu details are:%+v\n", vishnu)
fmt.Printf("Name is %v and email is %v\n", vishnu.Name, vishnu.Email)
vishnu.GetStatus()
vishnu.NewMail()
}
type User struct {
Name string
Email string
status bool
Age int
}
// method syntax
func (u User) GetStatus() {
fmt.Println("Is user active:", u.status)
}
func (u User) NewMail() { //this doesnt change the original value its only the copy of the value
u.Email = "test@go.dev"
fmt.Println("Email of this user is ", u.Email)
}
Methods are functions that are created for structs and used by struct objects
Below is the syntax to declare /define a method
func (u User) GetStatus() {
fmt.Println("Is user active:", u.status)
}
Any manipulation by method is not reflected in the original value.
The Methods can be built using struct or non struct but not the int or string type and the type should be present in same file
package main
import "fmt"
// using struct
type Person struct{
name string
age int
}
//thie Person is receiver where we can access the person details in display method
func (p Person) display(){
fmt.Println(p.name)
fmt.Println(p.age)
}
func main(){
person:= Person{name:'Vishnu',age:26}
person.display()
}
//output
Vishnu
26
Using Non struct types
package main
import "fmt"
//non struct type
type number int
func (n number) square() number{
return n*n
}
func main(){
num:=number(5)
fmt.Println(num.square())
}
// output
25
Using the pointer types
package main
import "fmt"
type Employee struct{
name string
age int
}
// sending pointer as reciver to make changes at ref not a copy
func (e *Employee) changeName(changedName string){
e.name=changedName
}
func (e Employee) changeNameWithOutPointer(cName string){
e.name=cName
}
func main (){
emp:= Employee{name:"Vishnu",age:26}
fmt.Println("Before",emp.name)
emp.changeName("Vish")
emt.Println("After",emp.name)
empwithoutpointer:= Employee{name:"Anusha",age:26}
fmt.Println("Before",empwithoutpointer.name)
empwithoutpointer.changeNameWithOutPointer("anu")
fmt.Println("After",empwithoutpointer.name)
}
//ouput
Vishnu
vish
Anusha
Anusha // here this will not change because it has not sent the ref , its send the copy value
// to the reciver so the copied value will be changed but not the original ref value
Defer
The defer in Go is used to delay the execution of a function or statement until the surrounding function returns.
package main
import "fmt"
func main() {
//two,one,world Last in first out
defer fmt.Println("World")
defer fmt.Println("One")
defer fmt.Println("Two")
fmt.Println("Hello")
myDefer()
}
func myDefer() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
every statement written with defer
will be executed last and if too many statements use the defer then the execution is done as last in first out. which means the last statement with defer
will be executed first.
Files
package main
import (
"fmt"
"io"
"os"
)
func main() {
fmt.Println("Welcome to files in golang")
content := "This needs to go in a file -Vishnu"
file, err := os.Create("./myfile.txt")
// if err != nil {
// panic(err)
// }
checkNilError(err)
length, err := io.WriteString(file, content)
// if err != nil {
// panic(err)
// }
checkNilError(err)
fmt.Println("Length is :", length)
defer file.Close()
readFile("./myfile.txt")
}
func readFile(filename string) {
dataByte, err := os.ReadFile(filename) //data inside the file is returned as buffer or bytes
checkNilError(err)
fmt.Println("Text data inside the file is \n", string(dataByte))
}
func checkNilError(err error) {
if err != nil {
panic(err)
}
}
to create file
file, err := os.Create("./myfile.txt")
to add content to file.
length, err := io.WriteString(file, content)
read data from file
dataByte, err := os.ReadFile(filename)
The data that outputs from file will be in byte we need to convert it to string as shown in the example above
Webreq
package main
import (
"fmt"
"io"
"net/http"
)
const url = "https://lco.dev"
func main() {
fmt.Println("golang web request")
response, err := http.Get(url)
if err != nil {
panic(err)
}
fmt.Printf("Response if of type:%T\n", response)
defer response.Body.Close() //closing is mandatory
dataBytes, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
content := string(dataBytes)
fmt.Println(content)
}
In this example, we are using http
package to send and receive web requests
We are getting responses from url from http
get method
response, err := http.Get(url)
Closing the response is mandatory
defer response.Body.Close()
io package is used to read the response as shown below
dataBytes, err := io.ReadAll(response.Body)
Structuring and destructuring Url
package main
import (
"fmt"
"net/url"
)
const myurl string = "https://lco.dev:3000/learn?coursename=reactjs&paymentid=ghbj52jsn"
func main() {
fmt.Println("Url in golang")
fmt.Println(myurl)
//parsing
result, _ := url.Parse(myurl)
// fmt.Println(result.Scheme)
// fmt.Println(result.Host)
// fmt.Println(result.Path)
// fmt.Println(result.Port())
// fmt.Println(result.RawQuery)
qparams := result.Query()
fmt.Printf("the type of query parms %T\n", qparams)
fmt.Println(qparams["coursename"])
for _, value := range qparams {
fmt.Println("Param is :", value)
}
partsOfUrl := &url.URL{
Scheme: "https",
Host: "lco.dev",
Path: "/tutcss",
RawPath: "user=vishnu",
}
anotherURL := partsOfUrl.String()
fmt.Println(anotherURL)
}
url package is used to read or write urls
as shown below we can use parse to get all parts of url
result, _ := url.Parse(myurl)
url is also used to build urls as shown below
partsOfUrl := &url.URL{
Scheme: "https",
Host: "lco.dev",
Path: "/tutcss",
RawPath: "user=vishnu",
}
Go routines
In this going to learn about multi threading in go lang
concurrency and Parlellism
In concurrency, we are using only 1 core to execute a task which takes time but in parallelism, we will be using n no of cores to run tasks and save time
package main
import (
"fmt"
"net/http"
"sync"
)
var signals = []string{"test"}
var wg sync.WaitGroup //pointers usually
var mut sync.Mutex //pointers usally
func main() {
fmt.Println("This is for goroutines in golang")
// go greeter("hello")
// greeter("World")
websitelist := []string{
"https://lco.dev",
"https://go.dev",
"https://google.com",
"https://github.com",
}
for _, web := range websitelist {
go getStatusCode(web)
wg.Add(1) //
}
fmt.Println(signals)
wg.Wait() // wait till the threads are back
}
// func greeter(s string) {
// // for i := 0; i < 6; i++ {
// // time.Sleep(3 * time.Millisecond)
// // fmt.Println(s)
// // }
// }
func getStatusCode(endpoint string) {
defer wg.Done() //send singal done
res, err := http.Get(endpoint)
if err != nil {
fmt.Println("Oops in endpoint")
} else {
mut.Lock()
signals = append(signals, endpoint)
mut.Unlock()
fmt.Printf("%d status code for %s\n", res.StatusCode, endpoint)
}
}
For using multiple threads we will use go keywords to assign tasks to different cores
We need to wait till the core comes with the response for that we are using sync
package
we have wait
add
and done
The done is called when the task is done the wait
will wait till the done is called the add
will count for different cores
Mutex
A mutex in Go is a synchronization mechanism that allows you to control access to shared resources. Mutexes are useful for preventing race conditions, which can occur when multiple goroutines are trying to access the same shared resource at the same time.
To use a mutex, you first need to create a new mutex object. You can do this using the sync.Mutex
type. Once you have created a mutex object, you can then use the Lock()
and Unlock()
methods to control access to the shared resource.
The Lock()
method blocks until the mutex is available. Once the mutex is available, the Lock()
method returns and the goroutine is allowed to access the shared resource.
The Unlock()
method releases the lock on the mutex. Once the lock is released, other goroutines are allowed to access the shared resource.
package main
import (
"fmt"
"sync"
)
func main() {
fmt.Println("Race condition - lco.in")
wg := &sync.WaitGroup{}
mut := &sync.Mutex{} //also check RWMutex
var score = []int{0}
wg.Add(3)
go func(wg *sync.WaitGroup, m *sync.Mutex) {
fmt.Println("one Routine")
mut.Lock()
score = append(score, 1)
mut.Unlock()
wg.Done()
}(wg, mut)
go func(wg *sync.WaitGroup, m *sync.Mutex) {
fmt.Println("Two Routine")
mut.Lock()
score = append(score, 2)
mut.Unlock()
wg.Done()
}(wg, mut)
go func(wg *sync.WaitGroup, m *sync.Mutex) {
fmt.Println("Three Routine")
mut.Lock()
score = append(score, 3)
mut.Unlock()
wg.Done()
}(wg, mut)
wg.Wait()
fmt.Println(score)
}
//command to run the program in race condition < go run --race . >
If we get an error in race conditions then there is a problem with the code.
Channels
Channels in Golang are a way for goroutines to communicate with each other. Channels are similar to queues, but they are more efficient because they are implemented using buffered communication.
To create a channel, you use the chan
keyword followed by the type of data that the channel will carry. For example, to create a channel that carries integers, you would use the following code:
ch := make(chan int)
Once you have created a channel, you can send and receive data from the channel using the <-
operator. To send data to a channel, you use the following syntax:
ch <- 10
To receive data from a channel, you use the following syntax:
v := <-ch
The <-
operator blocks until there is data to send or receive. If the channel is empty, the <-
operator will block until there is data to receive. If the channel is full, the <-
operator will block until there is room to send data.
Channels can be used to implement a variety of different communication patterns, such as producer-consumer, request-response, and publish-subscribe.
Here is an example of a simple producer-consumer program using channels:
package main
import (
"fmt"
"time"
)
func producer(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
time.Sleep(time.Second)
}
close(ch)
}
func consumer(ch chan int) {
for i := range ch {
fmt.Println(i)
}
}
func main() {
ch := make(chan int)
go producer(ch)
go consumer(ch)
time.Sleep(time.Second * 10)
}
Output:
0
1
2
3
4
5
6
7
8
9
In this example, the producer()
function sends 10 integers to the channel. The consumer()
function receives the integers from the channel and prints them to the console.
Channels are a powerful tool for communication between goroutines. They are easy to use and can be used to implement a variety of different communication patterns.
Here are some tips for using channels in Golang:
Use channels to communicate between goroutines.
Use the
chan
keyword to create a channel.Use the
<-
operator to send and receive data from a channel.Be careful not to block on a channel for too long. This can block other goroutines from accessing the channel.
Use defer statements to ensure that channels are always closed, even if an error occurs.
Channels are an essential tool for any Go programmer. They can help you to write more concurrent and efficient code.
We can also use sync wait groups instead of time as shown below
package main
import (
"fmt"
"sync"
)
func main() {
fmt.Println("Channels in golang -lco.in")
myChannel := make(chan int, 2)
wg := &sync.WaitGroup{}
// myChannel <- 5
// fmt.Println(<-myChannel)
wg.Add(2)
//receive only
go func(ch <-chan int, wg *sync.WaitGroup) {
//fmt.Println(<-myChannel)
//fmt.Println(<-myChannel)
val, isChanelOpen := <-myChannel
fmt.Println(isChanelOpen)
fmt.Println(val)
wg.Done()
}(myChannel, wg)
//send only
go func(ch chan<- int, wg *sync.WaitGroup) {
myChannel <- 5
myChannel <- 6
close(myChannel)
wg.Done()
}(myChannel, wg)
wg.Wait()
}
//we can only dump value in to channel when there is listener
Hope you had a wonderful Learning
Thank you