Golang-handledning
Vad är Go?
Go (även känt som Golang) är ett programmeringsspråk med öppen källkod som utvecklats av Google. Det är ett statiskt skrivet kompilerat språk. Go stöder samtidig programmering, dvs det tillåter körning av flera processer samtidigt. Detta uppnås med hjälp av kanaler, goroutiner, etc. Go Language har skräpinsamling som själv sköter minneshanteringen och tillåter uppskjuten exekvering av funktioner.
Vi kommer att lära oss alla grunderna i Golang i denna Learn Go Language Tutorial.
Hur man laddar ner och installerar GO
Steg 1) Gå till https://golang.org/dl/. Ladda ner binären för ditt operativsystem.
Steg 2) Double klicka på installationsprogrammet och klicka på Kör.
Steg 3) Klicka på Nästa
Steg 4) Välj installationsmappen och klicka på Nästa.
Steg 5) Klicka på Slutför när installationen är klar.
Steg 6) När installationen är klar kan du verifiera den genom att öppna terminalen och skriva
go version
Detta kommer att visa versionen av go installerad
Ditt First Go-program – Go Hello World!
Skapa en mapp som heter studyGo. I den här Go-språkhandledningen kommer vi att skapa våra go-program i den här mappen. Go-filer skapas med tillägget .gå. Du kan köra Go-program med syntaxen
go run <filename>
Skapa en fil som heter first.go och lägg till koden nedan i den och spara
package main import ("fmt") func main() { fmt.Println("Hello World! This is my first Go program\n") }
Navigera till den här mappen i din terminal. Kör programmet med kommandot
gå springa först.gå
Du kan se utskriften
Hello World! This is my first Go program
Låt oss nu diskutera programmet ovan.
huvudpaket – Varje Go Language-program bör börja med ett paketnamn. Go tillåter oss att använda paket i andra go-program och stöder därför återanvändbarhet av kod. Körning av ett Go-program börjar med koden inuti paketet som heter main.
import fmt – importerar paketet fmt. Detta paket implementerar I/O-funktionerna.
func main() – Detta är funktionen från vilken programkörningen börjar. Huvudfunktionen ska alltid placeras i huvudpaketet. Under main() kan du skriva koden inuti { }.
fmt.Println – Detta kommer att skriva ut texten på skärmen med funktionen Println i fmt.
Obs: När du nämner köra/kör koden i avsnitten nedan i denna Go-handledning betyder det att spara koden i en fil med .go-tillägget och köra den med syntaxen
go run <filename>
Datatyper
Typer (datatyper) representerar typen av värde som lagras i en variabel, typ av värde en funktion returnerar, etc.
Det finns tre grundläggande typer i Go Language
Numeriska typer – Representerar numeriska värden som inkluderar heltal, flyttal och komplexa värden. Olika numeriska typer är:
int8 – 8 bitars signerade heltal.
int16 – 16 bitars signerade heltal.
int32 – 32 bitars signerade heltal.
int64 – 64 bitars signerade heltal.
uint8 – 8-bitars heltal utan tecken.
uint16 – 16-bitars heltal utan tecken.
uint32 – 32-bitars heltal utan tecken.
uint64 – 64-bitars heltal utan tecken.
flytande 32 – 32 bitars flyttal.
flytande 64 – 64 bitars flyttal.
complex64 – har float32 verkliga och imaginära delar.
complex128 – har float32 verkliga och imaginära delar.
Strängtyper – Representerar en sekvens av byte(tecken). Du kan göra olika operationer på strängar som strängsammansättning, extrahera delsträng, etc
booleska typer – Representerar 2 värden, antingen sant eller falskt.
Golang-gränssnitt
Golang-gränssnitt är en samling metodsignaturer som används av en Type för att implementera beteendet hos objekt. Huvudmålet med Golang-gränssnittet är att tillhandahålla metodsignaturer med namn, argument och returtyper. Det är upp till en typ att deklarera och implementera metoden. Ett gränssnitt i Golang kan deklareras med nyckelordet "gränssnitt".
variabler
Variabler pekar på en minnesplats som lagrar något slags värde. Typparametern (i syntaxen nedan) representerar den typ av värde som kan lagras i minnesplatsen.
Variabel kan deklareras med hjälp av syntaxen
var <variable_name> <type>
När du deklarerar en variabel av en typ kan du tilldela variabeln till vilket värde som helst av den typen.
Du kan också ge ett initialvärde till en variabel under själva deklarationen med hjälp av
var <variable_name> <type> = <value>
Om du deklarerar variabeln med ett initialt värde, Go och härleda typen av variabel från den typ av värde som tilldelats. Så du kan utelämna typen under deklarationen med hjälp av syntaxen
var <variable_name> = <value>
Du kan också deklarera flera variabler med syntaxen
var <variable_name1>, <variable_name2> = <value1>, <value2>
Nedanstående program i denna Go-handledning har några Golang-exempel på variabeldeklarationer
package main import "fmt" func main() { //declaring a integer variable x var x int x=3 //assigning x the value 3 fmt.Println("x:", x) //prints 3 //declaring a integer variable y with value 20 in a single statement and prints it var y int=20 fmt.Println("y:", y) //declaring a variable z with value 50 and prints it //Here type int is not explicitly mentioned var z=50 fmt.Println("z:", z) //Multiple variables are assigned in single line- i with an integer and j with a string var i, j = 100,"hello" fmt.Println("i and j:", i,j) }
Resultatet blir
x: 3 y: 20 z: 50 i and j: 100 hello
Go Language ger också ett enkelt sätt att deklarera variablerna med värde genom att utelämna nyckelordet var
<variable_name> := <value>
Observera att du använde := istället för =. Du kan inte använda := bara för att tilldela ett värde till en variabel som redan är deklarerad. := används för att deklarera och tilldela värde.
Skapa en fil som heter assign.go med följande kod
package main import ("fmt") func main() { a := 20 fmt.Println(a) //gives error since a is already declared a := 30 fmt.Println(a) }
Kör go run assign.go för att se resultatet som
./assign.go:7:4: no new variables on left side of :=
Variabler som deklareras utan ett initialt värde kommer att ha 0 för numeriska typer, false för booleska och tom sträng för strängar
Konstant
Konstanta variabler är de variabler vars värde inte kan ändras när de väl har tilldelats. En konstant i Go programmeringsspråk deklareras genom att använda nyckelordet "const"
Skapa en fil som heter constant.go och med följande kod
package main import ("fmt") func main() { const b =10 fmt.Println(b) b = 30 fmt.Println(b) }
Kör go run constant.go för att se resultatet som
.constant.go:7:4: cannot assign to b
För loopexempel
Slingor används för att exekvera ett block av satser upprepade gånger baserat på ett villkor. De flesta av programmeringsspråken tillhandahåller 3 typer av loopar – för, medan, do while. Men programmeringsspråket Go stöder endast för loop.
Syntaxen för en Golang för loop är
for initialisation_expression; evaluation_expression; iteration_expression{ // one or more statement }
Initialiseringsuttrycket exekveras först (och endast en gång) i Golang for loop.
Sedan utvärderas evaluation_expression och om det är sant exekveras koden inuti blocket.
Iteration_expression id exekveras och evaluation_expression utvärderas igen. Om det är sant exekveras satsblocket igen. Detta kommer att fortsätta tills evaluation_expression blir falskt.
Kopiera programmet nedan till en fil och kör det för att se Golang för looputskriftsnummer från 1 till 5
package main import "fmt" func main() { var i int for i = 1; i <= 5; i++ { fmt.Println(i) } }
Output är
1 2 3 4 5
Om annat
Om annat är ett villkorligt uttalande. Synaxen är
if condition{ // statements_1 }else{ // statements_2 }
Här utvärderas villkoret och om det är sant kommer statements_1 att exekveras annars kommer statements_2 att exekveras.
Du kan använda if-satsen utan annat också. Du kan också ha kedjade if else-uttalanden. Nedanstående program kommer att förklara mer om annat.
Kör programmet nedan. Den kontrollerar om ett tal, x, är mindre än 10. Om så är fallet kommer det att skriva ut "x är mindre än 10"
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x < 10 fmt.Println("x is less than 10") } }
Eftersom värdet på x här är större än 10 kommer inte satsen inuti if-blocket att köras.
Se nu programmet nedan. I den här guiden för programmeringsspråket Go har vi ett annat block som kommer att köras om utvärderingen misslyckas.
package main import "fmt" func main() { var x = 50 if x < 10 { //Executes if x is less than 10 fmt.Println("x is less than 10") } else { //Executes if x >= 10 fmt.Println("x is greater than or equals 10") } }
Detta program kommer att ge dig utdata
x is greater than or equals 10
Nu i denna Go-handledning kommer vi att se ett program med flera if else-block (kedjade om annat). Utför Go-exemplet nedan. Den kontrollerar om ett tal är mindre än 10 eller är mellan 10-90 eller större än 90.
package main import "fmt" func main() { var x = 100 if x < 10 { //Executes if x is less than 10 fmt.Println("x is less than 10") } else if x >= 10 && x <= 90 { //Executes if x >= 10 and x<=90 fmt.Println("x is between 10 and 90") } else { //Executes if both above cases fail i.e x>90 fmt.Println("x is greater than 90") } }
Här kontrollerar först if-villkoret om x är mindre än 10 och inte. Så det kontrollerar nästa villkor (annat om) om det är mellan 10 och 90, vilket också är falskt. Så den exekverar sedan blocket under else-sektionen som ger utdata
x is greater than 90
Växla
Switch är ett annat villkorligt uttalande. Switch-satser utvärderar ett uttryck och resultatet jämförs med en uppsättning tillgängliga värden (fall). När en matchning har hittats exekveras uttalanden som är associerade med matchningen (casefallet). Om ingen matchning hittas kommer ingenting att exekveras. Du kan också lägga till ett standardfall för växling som kommer att köras om inga andra matchningar hittas. Syntaxen för switchen är
switch expression { case value_1: statements_1 case value_2: statements_2 case value_n: statements_n default: statements_default }
Här jämförs uttryckets värde mot värdena i varje enskilt fall. När en matchning har hittats exekveras de uttalanden som är associerade med det fallet. Om ingen matchning hittas exekveras satserna under standardavsnittet.
Kör programmet nedan
package main import "fmt" func main() { a,b := 2,1 switch a+b { case 1: fmt.Println("Sum is 1") case 2: fmt.Println("Sum is 2") case 3: fmt.Println("Sum is 3") default: fmt.Println("Printing default") } }
Du kommer att få utdata som
Sum is 3
Ändra värdet på a och b till 3 så blir resultatet
Printing default
Du kan också ha flera värden i ett fall genom att separera dem med ett kommatecken.
arrayer
Array representerar en fast storlek, namngiven sekvens av element av samma typ. Du kan inte ha en array som innehåller både heltal och tecken. Du kan inte ändra storleken på en array när du väl har definierat storleken.
Syntaxen för att deklarera en array är
var arrayname [size] type
Varje matriselement kan tilldelas värde med hjälp av syntaxen
arrayname [index] = value
Arrayindex börjar från 0 till storlek-1.
Du kan tilldela värden till matriselement under deklarationen med hjälp av syntaxen
arrayname := [size] type {value_0,value_1,…,value_size-1}
Du kan också ignorera storleksparametern medan du deklarerar arrayen med värden genom att ersätta storlek med . och kompilatorn kommer att hitta längden från antalet värden. Syntax är
arrayname := […] type {value_0,value_1,…,value_size-1}
Du kan hitta längden på arrayen genom att använda syntaxen
len(arrayname)
Utför Go-exemplet nedan för att förstå arrayen
package main import "fmt" func main() { var numbers [3] string //Declaring a string array of size 3 and adding elements numbers[0] = "One" numbers[1] = "Two" numbers[2] = "Three" fmt.Println(numbers[1]) //prints Two fmt.Println(len(numbers)) //prints 3 fmt.Println(numbers) // prints [One Two Three] directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements fmt.Println(directions) //prints [1 2 3 4 5] fmt.Println(len(directions)) //prints 5 //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array) //fmt.Println(directions[5]) }
Produktion
Two 3 [One Two Three] [1 2 3 4 5] 5
Golang Slice and Append-funktion
En skiva är en del eller ett segment av en array. Eller så är det en vy eller en delvy av en underliggande array som den pekar på. Du kan komma åt elementen i en skiva med hjälp av skivans namn och indexnummer precis som du gör i en array. Du kan inte ändra längden på en array, men du kan ändra storleken på en skiva.
Innehållet i en skiva är faktiskt pekarna till elementen i en array. Det betyder om du ändrar något element i en skiva kommer det underliggande arrayinnehållet också att påverkas.
Syntaxen för att skapa ett segment är
var slice_name [] type = array_name[start:end]
Detta kommer att skapa en skiva som heter slice_name från en array som heter array_name med elementen i indexet från början till slut-1.
Nu i denna Golang-handledning kommer vi att köra programmet nedan. Programmet kommer att skapa en skiva från arrayen och skriva ut den. Du kan också se att ändring av innehållet i segmentet kommer att ändra den faktiska arrayen.
package main import "fmt" func main() { // declaring array a := [5] string {"one", "two", "three", "four", "five"} fmt.Println("Array after creation:",a) var b [] string = a[1:4] //created a slice named b fmt.Println("Slice after creation:",b) b[0]="changed" // changed the slice data fmt.Println("Slice after modifying:",b) fmt.Println("Array after slice modification:",a) }
Detta kommer att skriva ut resultatet som
Array after creation: [one two three four five] Slice after creation: [two three four] Slice after modifying: [changed three four] Array after slice modification: [one changed three four five]
Det finns vissa funktioner som Golang len, Golang append som du kan använda på skivor
len(slice_name) – returnerar längden på skivan
append(slice_name, value_1, value_2) – Golang append används för att lägga till value_1 och value_2 till ett befintligt segment.
append(slice_nale1,slice_name2...) – lägger till skivnamn2 till skivnamn1
Kör följande program.
package main import "fmt" func main() { a := [5] string {"1","2","3","4","5"} slice_a := a[1:3] b := [5] string {"one","two","three","four","five"} slice_b := b[1:3] fmt.Println("Slice_a:", slice_a) fmt.Println("Slice_b:", slice_b) fmt.Println("Length of slice_a:", len(slice_a)) fmt.Println("Length of slice_b:", len(slice_b)) slice_a = append(slice_a,slice_b...) // appending slice fmt.Println("New Slice_a after appending slice_b :", slice_a) slice_a = append(slice_a,"text1") // appending value fmt.Println("New Slice_a after appending text1 :", slice_a) }
Resultatet blir
Slice_a: [2 3] Slice_b: [two three] Length of slice_a: 2 Length of slice_b: 2 New Slice_a after appending slice_b : [2 3 two three] New Slice_a after appending text1 : [2 3 two three text1]
Programmet skapar först 2 skivor och skrev ut dess längd. Sedan satte den en skiva till en annan och satte sedan en sträng till den resulterande skivan.
Funktioner
En funktion representerar ett block av satser som utför en specifik uppgift. En funktionsdeklaration berättar för oss funktionsnamn, returtyp och indataparametrar. Funktionsdefinition representerar koden som finns i funktionen. Syntaxen för att deklarera funktionen är
func function_name(parameter_1 type, parameter_n type) return_type { //statements }
Parametrarna och returtyperna är valfria. Du kan också returnera flera värden från en funktion.
Nu i denna Golang-handledning, låt oss köra följande Golang-exempel. Funktionen calc här accepterar 2 tal och utför addition och subtraktion och returnerar båda värdena.
package main import "fmt" //calc is the function name which accepts two integers num1 and num2 //(int, int) says that the function returns two values, both of integer type. func calc(num1 int, num2 int)(int, int) { sum := num1 + num2 diff := num1 - num2 return sum, diff } func main() { x,y := 15,10 //calls the function calc with x and y an d gets sum, diff as output sum, diff := calc(x,y) fmt.Println("Sum",sum) fmt.Println("Diff",diff) }
Resultatet blir
Sum 25 Diff 5
Paket
Paket används för att organisera koden. I ett stort projekt är det inte möjligt att skriva kod i en enda fil. Go programmeringsspråk låter oss organisera koden under olika paket. Detta ökar kodläsbarheten och återanvändbarheten. Ett körbart Go-program bör innehålla ett paket med namnet main och programkörningen startar från funktionen som heter main. Du kan importera andra paket i vårt program med hjälp av syntaxen
import package_name
Vi kommer att se och diskutera i denna Golang-handledning, hur man skapar och använder paket i följande Golang-exempel.
Steg 1) Skapa en fil som heter package_example.go och lägg till koden nedan
package main import "fmt" //the package to be created import "calculation" func main() { x,y := 15,10 //the package will have function Do_add() sum := calculation.Do_add(x,y) fmt.Println("Sum",sum) }
I programmet ovan finns fmt ett paket som Go-programmeringsspråket tillhandahåller oss främst för I/O-ändamål. Du kan också se ett paket som heter beräkning. Inuti main() kan du se en stegsumma := beräkning.Do_add(x,y). Det betyder att du anropar funktionen Do_add från paketberäkning.
Steg 2) Först bör du skapa paketberäkningen i en mapp med samma namn under src-mappen på vägen. Den installerade sökvägen för go kan hittas från PATH-variabeln.
För mac, hitta sökvägen genom att köra echo $PATH
Så sökvägen är /usr/local/go
För Windows, hitta sökvägen genom att köra echo %GOROOT%
Här är sökvägen C:\Go\
Steg 3) Navigera till mappen src (/usr/local/go/src för mac och C:\Go\src för Windows). Nu från koden är paketnamnet beräkning. Go kräver att paketet ska placeras i en katalog med samma namn under src-katalogen. Skapa en katalog som heter beräkning i src-mappen.
Steg 4) Skapa en fil som heter calc.go (Du kan ge vilket namn som helst, men paketnamnet i koden spelar roll. Här ska det vara beräkning) i beräkningskatalogen och lägg till koden nedan
package calculation func Do_add(num1 int, num2 int)(int) { sum := num1 + num2 return sum }
Steg 5) Kör kommandot go install från beräkningskatalogen som kommer att kompilera calc.go.
Steg 6) Gå nu tillbaka till package_example.go och kör go run package_example.go. Utgången blir summa 25.
Observera att namnet på funktionen Do_add börjar med en stor bokstav. Detta beror på att i Go om funktionsnamnet börjar med en stor bokstav betyder det att andra program kan se (åtkomst) det annars kan andra program inte komma åt det. Om funktionsnamnet var do_add , skulle du ha fått felet
kan inte referera till oexporterad namnberäkning.beräkning.
Defer och stapling defers
Defer-satser används för att skjuta upp exekveringen av ett funktionsanrop tills funktionen som innehåller defer-satsen slutför exekveringen.
Låt oss lära oss detta med ett exempel:
package main import "fmt" func sample() { fmt.Println("Inside the sample()") } func main() { //sample() will be invoked only after executing the statements of main() defer sample() fmt.Println("Inside the main()") }
Resultatet blir
Inside the main() Inside the sample()
Här skjuts exekveringen av sample() upp tills exekveringen av den omslutande funktionen(main()) är klar.
Stacking defer använder flera defer-satser. Anta att du har flera defer-satser i en funktion. Go placerar alla uppskjutna funktionsanrop i en stack, och när den omslutande funktionen återvänder, exekveras de staplade funktionerna i Last In First Out (LIFO) order. Du kan se detta i exemplet nedan.
Kör koden nedan
package main import "fmt" func display(a int) { fmt.Println(a) } func main() { defer display(1) defer display(2) defer display(3) fmt.Println(4) }
Resultatet blir
4 3 2 1
Här exekveras först koden inuti main() och sedan exekveras de uppskjutna funktionsanropen i omvänd ordning, dvs 4, 3,2,1.
Pekare
Innan vi förklarar tips, låt oss först diskutera '&'-operatorn. Operatorn '&' används för att få adressen till en variabel. Det betyder att '&a' kommer att skriva ut minnesadressen för variabel a.
I den här Golang-handledningen kommer vi att köra programmet nedan för att visa värdet på en variabel och adressen till den variabeln
package main import "fmt" func main() { a := 20 fmt.Println("Address:",&a) fmt.Println("Value:",a) }
Resultatet blir
Address: 0xc000078008 Value: 20
En pekarvariabel lagrar minnesadressen för en annan variabel. Du kan definiera en pekare med hjälp av syntaxen
var variable_name *type
Asterisken(*) representerar variabeln är en pekare. Du kommer att förstå mer genom att köra programmet nedan
package main import "fmt" func main() { //Create an integer variable a with value 20 a := 20 //Create a pointer variable b and assigned the address of a var b *int = &a //print address of a(&a) and value of a fmt.Println("Address of a:",&a) fmt.Println("Value of a:",a) //print b which contains the memory address of a i.e. &a fmt.Println("Address of pointer b:",b) //*b prints the value in memory address which b contains i.e. the value of a fmt.Println("Value of pointer b",*b) //increment the value of variable a using the variable b *b = *b+1 //prints the new value using a and *b fmt.Println("Value of pointer b",*b) fmt.Println("Value of a:",a)}
Resultatet blir
Address of a: 0x416020 Value of a: 20 Address of pointer b: 0x416020 Value of pointer b 20 Value of pointer b 21 Value of a: 21
Strukturer
En struktur är en användardefinierad datatyp som i sig innehåller ytterligare ett element av samma eller annan typ.
Att använda en struktur är en process i två steg.
Skapa (deklarera) först en strukturtyp
För det andra, skapa variabler av den typen för att lagra värden.
Strukturer används främst när du vill lagra relaterad data tillsammans.
Tänk på en del av anställd information som har namn, ålder och adress. Du kan hantera detta på 2 sätt
Skapa 3 arrayer – en array lagrar namnen på anställda, en lagrar ålder och den tredje lagrar ålder.
Deklarera en strukturtyp med 3 fält-namn, adress och ålder. Skapa en array av den strukturtypen där varje element är ett strukturobjekt med namn, adress och ålder.
Det första tillvägagångssättet är inte effektivt. I den här typen av scenarier är strukturer mer bekväma.
Syntaxen för att deklarera en struktur är
type structname struct { variable_1 variable_1_type variable_2 variable_2_type variable_n variable_n_type }
Ett exempel på en strukturdeklaration är
type emp struct { name string address string age int }
Här skapas en ny användardefinierad typ som heter emp. Nu kan du skapa variabler av typen emp med hjälp av syntaxen
var variable_name struct_name
Ett exempel är
var empdata1 emp
Du kan ställa in värden för empdata1 som
empdata1.name = "John" empdata1.address = "Street-1, Bangalore" empdata1.age = 30
Du kan också skapa en strukturvariabel och tilldela värden genom
empdata2 := emp{"Raj", "Building-1, Delhi", 25}
Här måste du behålla ordningen på elementen. Raj kommer att mappas till namn, nästa element till adress och det sista att åldras.
Kör koden nedan
package main import "fmt" //declared the structure named emp type emp struct { name string address string age int } //function which accepts variable of emp type and prints name property func display(e emp) { fmt.Println(e.name) } func main() { // declares a variable, empdata1, of the type emp var empdata1 emp //assign values to members of empdata1 empdata1.name = "John" empdata1.address = "Street-1, London" empdata1.age = 30 //declares and assign values to variable empdata2 of type emp empdata2 := emp{"Raj", "Building-1, Paris", 25} //prints the member name of empdata1 and empdata2 using display function display(empdata1) display(empdata2) }
Produktion
John Raj
Metoder (inte funktioner)
En metod är en funktion med ett mottagarargument. Archirent tekniskt står det mellan nyckelordet func och metodnamnet. Syntaxen för en metod är
func (variable variabletype) methodName(parameter1 paramether1type) { }
Låt oss konvertera ovanstående exempelprogram till att använda metoder istället för funktion.
package main import "fmt" //declared the structure named emp type emp struct { name string address string age int } //Declaring a function with receiver of the type emp func(e emp) display() { fmt.Println(e.name) } func main() { //declaring a variable of type emp var empdata1 emp //Assign values to members empdata1.name = "John" empdata1.address = "Street-1, Lodon" empdata1.age = 30 //declaring a variable of type emp and assign values to members empdata2 := emp { "Raj", "Building-1, Paris", 25} //Invoking the method using the receiver of the type emp // syntax is variable.methodname() empdata1.display() empdata2.display() }
Go är inte ett objektorienterat språk och det har inte begreppet klass. Metoder ger en känsla av vad du gör i objektorienterade program där funktionerna i en klass anropas med hjälp av syntaxen objektnamn.funktionsnamn()
samtidighet
Go stöder samtidig utförande av uppgifter. Det betyder att Go kan utföra flera uppgifter samtidigt. Det skiljer sig från begreppet parallellism. I parallellism delas en uppgift upp i små deluppgifter och utförs parallellt. Men samtidigt utförs flera uppgifter samtidigt. Samtidighet uppnås i Go med Goroutines och Channels.
Goroutiner
En goroutin är en funktion som kan köras samtidigt med andra funktioner. Vanligtvis när en funktion anropas överförs kontrollen till den anropade funktionen, och när dess slutförda exekveringskontroll återgår till den anropande funktionen. Den anropande funktionen fortsätter sedan att utföras. Den anropande funktionen väntar på att den anropade funktionen slutför exekveringen innan den fortsätter med resten av satserna.
Men i fallet med goroutine, kommer den anropande funktionen inte att vänta på att exekveringen av den anropade funktionen ska slutföras. Det kommer att fortsätta att köras med nästa uttalanden. Du kan ha flera goroutiner i ett program.
Dessutom kommer huvudprogrammet att avslutas när det slutfört exekveringen av sina uttalanden och det kommer inte att vänta på slutförandet av de anropade goroutinerna.
Goroutine anropas med nyckelordet go följt av ett funktionsanrop.
Exempelvis
go add(x,y)
Du kommer att förstå goroutiner med Golang-exemplen nedan. Kör programmet nedan
package main import "fmt" func display() { for i:=0; i<5; i++ { fmt.Println("In display") } } func main() { //invoking the goroutine display() go display() //The main() continues without waiting for display() for i:=0; i<5; i++ { fmt.Println("In main") } }
Resultatet blir
In main In main In main In main In main
Här avslutade huvudprogrammet exekveringen redan innan goroutinen startade. Display() är en goroutin som anropas med hjälp av syntaxen
go function_name(parameter list)
I ovanstående kod väntar main() inte på att display() ska slutföras, och main() slutförde sin körning innan display() exekverade sin kod. Så print-satsen inuti display() skrivs inte ut.
Nu modifierar vi programmet för att även skriva ut satserna från display(). Vi lägger till en tidsfördröjning på 2 sek i for-slingan i main() och en 1 sek fördröjning i for-slingan på display().
package main import "fmt" import "time" func display() { for i:=0; i<5; i++ { time.Sleep(1 * time.Second) fmt.Println("In display") } } func main() { //invoking the goroutine display() go display() for i:=0; i<5; i++ { time.Sleep(2 * time.Second) fmt.Println("In main") } }
Utgången kommer att vara något liknande
In display In main In display In display In main In display In display In main In main In main
Här kan du se att båda looparna exekveras på ett överlappande sätt på grund av den samtidiga exekveringen.
Kanaler
Kanaler är ett sätt för funktioner att kommunicera med varandra. Det kan ses som ett medium där en rutin placerar data och nås av en annan rutin i Golang-servern.
En kanal kan deklareras med syntaxen
channel_variable := make(chan datatype)
Exempelvis:
ch := make(chan int)
Du kan skicka data till en kanal med hjälp av syntaxen
channel_variable <- variable_name
Exempelvis
ch <- x
Du kan ta emot data från en kanal med hjälp av syntaxen
variable_name := <- channel_variable
Exempelvis
y := <- ch
I ovanstående Go-språkexempel på goroutine har du sett att huvudprogrammet inte väntar på goroutinen. Men så är det inte när kanaler är inblandade. Anta att om en goroutin skjuter data till kanal, kommer main() att vänta på satsen som tar emot kanaldata tills den får data.
Du kommer att se detta i exempel på Go-språk nedan. Skriv först en normal goroutin och se beteendet. Ändra sedan programmet för att använda kanaler och se beteendet.
Kör programmet nedan
package main import "fmt" import "time" func display() { time.Sleep(5 * time.Second) fmt.Println("Inside display()") } func main() { go display() fmt.Println("Inside main()") }
Resultatet blir
Inside main()
Main() avslutade exekveringen och avslutade innan goroutinen kördes. Så utskriften inuti display() kördes inte.
Ändra nu programmet ovan för att använda kanaler och se beteendet.
package main import "fmt" import "time" func display(ch chan int) { time.Sleep(5 * time.Second) fmt.Println("Inside display()") ch <- 1234 } func main() { ch := make(chan int) go display(ch) x := <-ch fmt.Println("Inside main()") fmt.Println("Printing x in main() after taking from channel:",x) }
Resultatet blir
Inside display() Inside main() Printing x in main() after taking from channel: 1234
Här vad som händer är main() när man når x := <-ch väntar på data på kanal ch. Displayen() har en väntetid på 5 sekunder och skickar sedan data till kanalen ch. Main() för att ta emot data från kanalen avblockeras och fortsätter dess exekvering.
Avsändaren som skickar data till kanalen kan informera mottagarna att ingen mer data kommer att läggas till kanalen genom att stänga kanalen. Detta används främst när du använder en loop för att skicka data till en kanal. En kanal kan stängas med
close(channel_name)
Och i mottagaränden är det möjligt att kontrollera om kanalen är stängd med hjälp av en extra variabel medan data hämtas från kanalen med
variable_name, status := <- channel_variable
Om statusen är True betyder det att du fått data från kanalen. Om falskt betyder det att du försöker läsa från en stängd kanal
Du kan också använda kanaler för kommunikation mellan goroutiner. Behöver använda 2 goroutiner – en skickar data till kanalen och andra tar emot data från kanalen. Se programmet nedan
package main import "fmt" import "time" //This subroutine pushes numbers 0 to 9 to the channel and closes the channel func add_to_channel(ch chan int) { fmt.Println("Send data") for i:=0; i<10; i++ { ch <- i //pushing data to channel } close(ch) //closing the channel } //This subroutine fetches data from the channel and prints it. func fetch_from_channel(ch chan int) { fmt.Println("Read data") for { //fetch data from channel x, flag := <- ch //flag is true if data is received from the channel //flag is false when the channel is closed if flag == true { fmt.Println(x) }else{ fmt.Println("Empty channel") break } } } func main() { //creating a channel variable to transport integer values ch := make(chan int) //invoking the subroutines to add and fetch from the channel //These routines execute simultaneously go add_to_channel(ch) go fetch_from_channel(ch) //delay is to prevent the exiting of main() before goroutines finish time.Sleep(5 * time.Second) fmt.Println("Inside main()") }
Här finns det 2 subrutiner en skjuter data till kanalen och andra skriver ut data till kanalen. Funktionen add_to_channel adderar siffrorna från 0 till 9 och stänger kanalen. Samtidigt väntar funktionen hämta_från_kanal kl
x, flagga := <- ch och när data blir tillgängliga skrivs data ut. Den avslutas när flaggan är falsk vilket betyder att kanalen är stängd.
Väntan i main() ges för att förhindra att main() lämnas tills goroutinerna avslutar exekveringen.
Kör koden och se utdata som
Read data Send data 0 1 2 3 4 5 6 7 8 9 Empty channel Inside main()
Välja
Select kan ses som en switch-sats som fungerar på kanaler. Här kommer fallutlåtandena att vara en kanaloperation. Vanligtvis kommer varje fall uttalanden att läsas försök från kanalen. När något av fallen är klart (kanalen läses) exekveras uttalandet som är associerat med det ärendet. Om flera fall är klara kommer den att välja ett slumpmässigt. Du kan ha ett standardfall som körs om inget av fallen är klart.
Låt oss se koden nedan
package main import "fmt" import "time" //push data to channel with a 4 second delay func data1(ch chan string) { time.Sleep(4 * time.Second) ch <- "from data1()" } //push data to channel with a 2 second delay func data2(ch chan string) { time.Sleep(2 * time.Second) ch <- "from data2()" } func main() { //creating channel variables for transporting string values chan1 := make(chan string) chan2 := make(chan string) //invoking the subroutines with channel variables go data1(chan1) go data2(chan2) //Both case statements wait for data in the chan1 or chan2. //chan2 gets data first since the delay is only 2 sec in data2(). //So the second case will execute and exits the select block select { case x := <-chan1: fmt.Println(x) case y := <-chan2: fmt.Println(y) } }
Att köra programmet ovan ger resultatet:
from data2()
Här väntar select-satsen på att data ska vara tillgänglig i någon av kanalerna. Data2() lägger till data till kanalen efter en sömn på 2 sekunder vilket gör att det andra fallet körs.
Lägg till ett standardfall till markeringen i samma program och se resultatet. Här, när det väljs block, om inget fall har data redo på kanalen, kommer det att exekvera standardblocket utan att vänta på att data ska vara tillgänglig på någon kanal.
package main import "fmt" import "time" //push data to channel with a 4 second delay func data1(ch chan string) { time.Sleep(4 * time.Second) ch <- "from data1()" } //push data to channel with a 2 second delay func data2(ch chan string) { time.Sleep(2 * time.Second) ch <- "from data2()" } func main() { //creating channel variables for transporting string values chan1 := make(chan string) chan2 := make(chan string) //invoking the subroutines with channel variables go data1(chan1) go data2(chan2) //Both case statements check for data in chan1 or chan2. //But data is not available (both routines have a delay of 2 and 4 sec) //So the default block will be executed without waiting for data in channels. select { case x := <-chan1: fmt.Println(x) case y := <-chan2: fmt.Println(y) default: fmt.Println("Default case executed") } }
Detta program kommer att ge resultatet:
Default case executed
Detta beror på att när valblocket nådde hade ingen kanal data för läsning. Så standardfallet exekveras.
mutex
Mutex är kortformen för ömsesidig uteslutning. Mutex används när du inte vill tillåta att en resurs ska nås av flera subrutiner samtidigt. Mutex har 2 metoder - Lås och Lås upp. Mutex finns i sync-paketet. Så du måste importera synkroniseringspaketet. De satser som måste utföras ömsesidigt uteslutande kan placeras inuti mutex.Lock() och mutex.Unlock().
Låt oss lära oss mutex med ett exempel som räknar antalet gånger en loop exekveras. I det här programmet förväntar vi oss att rutinen körs loop 10 gånger och räkningen lagras i summa. Du anropar denna rutin 3 gånger så det totala antalet bör vara 30. Antalet lagras i en global variabelräkning.
Först kör du programmet utan mutex
package main import "fmt" import "time" import "strconv" import "math/rand" //declare count variable, which is accessed by all the routine instances var count = 0 //copies count to temp, do some processing(increment) and store back to count //random delay is added between reading and writing of count variable func process(n int) { //loop incrementing the count by 10 for i := 0; i < 10; i++ { time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) temp := count temp++ time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) count = temp } fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count)) } func main() { //loop calling the process() 3 times for i := 1; i < 4; i++ { go process(i) } //delay to wait for the routines to complete time.Sleep(25 * time.Second) fmt.Println("Final Count:", count) }
Se resultatet
Count after i=1 Count: 11 Count after i=3 Count: 12 Count after i=2 Count: 13 Final Count: 13
Resultatet kan bli annorlunda när du kör det men slutresultatet blir inte 30.
Det som händer här är att 3 goroutiner försöker öka antalet loopar som lagras i det variabla antalet. Anta att antalet vid ett ögonblick är 5 och goroutine1 kommer att öka antalet till 6. Huvudstegen inkluderar
Kopiera räkna till temp
Öka temp
Spara temperaturen tillbaka för att räkna
Antag att strax efter att ha utfört steg 3 av goroutine1; en annan goroutin kan ha ett gammalt värde, säg att 3 gör ovanstående steg och lagrar 4 tillbaka, vilket är fel. Detta kan förhindras genom att använda mutex som gör att andra rutiner väntar när en rutin redan använder variabeln.
Nu kommer du att köra programmet med mutex. Här exekveras de ovan nämnda 3 stegen i en mutex.
package main import "fmt" import "time" import "sync" import "strconv" import "math/rand" //declare a mutex instance var mu sync.Mutex //declare count variable, which is accessed by all the routine instances var count = 0 //copies count to temp, do some processing(increment) and store back to count //random delay is added between reading and writing of count variable func process(n int) { //loop incrementing the count by 10 for i := 0; i < 10; i++ { time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) //lock starts here mu.Lock() temp := count temp++ time.Sleep(time.Duration(rand.Int31n(2)) * time.Second) count = temp //lock ends here mu.Unlock() } fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count)) } func main() { //loop calling the process() 3 times for i := 1; i < 4; i++ { go process(i) } //delay to wait for the routines to complete time.Sleep(25 * time.Second) fmt.Println("Final Count:", count) }
Nu blir utgången
Count after i=3 Count: 21 Count after i=2 Count: 28 Count after i=1 Count: 30 Final Count: 30
Här får vi det förväntade resultatet som slutresultat. Eftersom uttalandena läser, inkrementerar och skriver tillbaka räkningen utförs i en mutex.
Felhantering
Fel är onormala tillstånd som att stänga en fil som inte är öppen, öppna en fil som inte finns, etc. Funktioner returnerar vanligtvis fel som det sista returvärdet.
Exemplet nedan förklarar mer om felet.
package main import "fmt" import "os" //function accepts a filename and tries to open it. func fileopen(name string) { f, er := os.Open(name) //er will be nil if the file exists else it returns an error object if er != nil { fmt.Println(er) return }else{ fmt.Println("file opened", f.Name()) } } func main() { fileopen("invalid.txt") }
Utgången kommer att vara:
open /invalid.txt: no such file or directory
Här försökte vi öppna en icke-existerande fil, och den returnerade felet till er variabel. Om filen är giltig kommer felet att vara null
Anpassade fel
Med den här funktionen kan du skapa anpassade fel. Detta görs genom att använda New() av felpaketet. Vi kommer att skriva om programmet ovan för att använda anpassade fel.
Kör programmet nedan
package main import "fmt" import "os" import "errors" //function accepts a filename and tries to open it. func fileopen(name string) (string, error) { f, er := os.Open(name) //er will be nil if the file exists else it returns an error object if er != nil { //created a new error object and returns it return "", errors.New("Custom error message: File name is wrong") }else{ return f.Name(),nil } } func main() { //receives custom error or nil after trying to open the file filename, error := fileopen("invalid.txt") if error != nil { fmt.Println(error) }else{ fmt.Println("file opened", filename) } }
Utgången kommer att vara:
Custom error message:File name is wrong
Här returnerar area() arean av en kvadrat. Om inmatningen är mindre än 1 returnerar area() ett felmeddelande.
Läser filer
Filer används för att lagra data. Go låter oss läsa data från filerna
Skapa först en fil, data.txt, i din nuvarande katalog med innehållet nedan.
Line one Line two Line three
Kör nu programmet nedan för att se att det skriver ut innehållet i hela filen som utdata
package main import "fmt" import "io/ioutil" func main() { data, err := ioutil.ReadFile("data.txt") if err != nil { fmt.Println("File reading error", err) return } fmt.Println("Contents of file:", string(data)) }
Här läser data, err := ioutil.ReadFile(“data.txt”) data och returnerar en bytesekvens. Under utskrift konverteras den till strängformat.
Skriver filer
Du kommer att se detta med ett program
package main import "fmt" import "os" func main() { f, err := os.Create("file1.txt") if err != nil { fmt.Println(err) return } l, err := f.WriteString("Write Line one") if err != nil { fmt.Println(err) f.Close() return } fmt.Println(l, "bytes written") err = f.Close() if err != nil { fmt.Println(err) return } }
Här skapas en fil, test.txt. Om filen redan finns trunkeras innehållet i filen. Writeline() används för att skriva innehållet till filen. Efter det stängde du filen med Close().
Fusklapp
I denna Go-handledning täckte vi,
Ämne | BESKRIVNING | syntax |
---|---|---|
Grundtyper | Numerisk, sträng, bool | |
variabler | Deklarera och tilldela värden till variabler | var variabelnamn typ var variabelnamn typ = värde var variabelnamn1, variabelnamn2 = värde1, värde2 variabelnamn := värde |
Konstant | Variabler vars värde inte kan ändras när de väl har tilldelats | const variabel = värde |
För Loop | Kör uttalanden i en loop. | för initialiseringsuttryck; evaluation_expression; iteration_expression{ // ett eller flera uttalanden } |
Om annat | Det är ett villkorligt uttalande | om skick{ // uttalanden_1 } Else { // uttalanden_2 } |
strömbrytare | Villkorligt uttalande med flera fall | switch expression { case value_1: uttalanden_1 case value_2: uttalanden_2 case value_n: uttalanden_n default: statements_default } |
array | Fast storlek med namnet sekvens av element av samma typ | arrayname := [size] typ {value_0,value_1,...,value_size-1} |
Skiva | Del eller segment av en array | var slice_name [] typ = array_name[start:slut] |
Funktioner | Block av uttalanden som utför en specifik uppgift | func function_name(parameter_1 typ, parameter_n type) return_type { //påståenden } |
Paket | Används för att organisera koden. Ökar kodläsbarhet och återanvändbarhet | importera paket_nam |
Skjut upp | Dröjer upp exekveringen av en funktion tills den innehållande funktionen avslutar exekveringen | defer function_name (parameter_list) |
Pekare | Lagrar minnesadressen för en annan variabel. | var variabelnamn *typ |
Structure | Användardefinierad datatyp som i sig innehåller ytterligare ett element av samma eller annan typ | typ structname struct { variabel_1 variabel_1_typ variabel_2 variabel_2_typ variabel_n variabel_n_typ } |
Metoder | En metod är en funktion med ett mottagarargument | func (variabel variabeltyp) methodName(parameter_list) { } |
Goroutine | En funktion som kan köras samtidigt med andra funktioner. | gå funktionsnamn(parameterlista) |
Kanal | Sätt för funktioner att kommunicera med varandra. Ett medium till vilket en rutin placerar data och nås av en annan rutin. | Förklara: ch := make(chan int) Skicka data till kanal: kanalvariabel <- variabelnamn Ta emot från kanal: variabelnamn := <- kanalvariabel |
Välja | Switch statement som fungerar på kanaler. Fallutlåtandena kommer att vara en kanaloperation. När någon av kanalerna är redo med data, exekveras uttalandet som är associerat med det fallet | Välj { fall x := <-chan1: fmt.Println(x) fall y := <-chan2: fmt.Println(y) } |
mutex | Mutex används när du inte vill tillåta att en resurs ska nås av flera subrutiner samtidigt. Mutex har 2 metoder - Lås och Lås upp | mutex.Lock() //påståenden mutex.Unlock(). |
Läs filer | Läser data och returnerar en bytesekvens. | Data, fel := ioutil.ReadFile(filnamn) |
Skriv fil | Skriver data till en fil | l, fel := f.WriteString(text_to_write) |