Suhteliselt uus Mine programmeerimiskeelt istub kenasti maastiku keskel, pakkudes palju häid omadusi ja jättes teadlikult välja paljud halvad. See kompileerib kiiresti, töötab kiiresti, sisaldab käitamis- ja prügivedu, sellel on lihtne staatilise tüübi süsteem ja dünaamilised liidesed ning suurepärane standardraamatukogu. Seetõttu soovivad nii paljud arendajad õppida Go-programmeerimist.
Vahepeal võtab Golang kindla positsiooni funktsioonide osas, mis võivad põhjustada segadust ja vigu. See jätab välja OOP idioomid, nagu pärimine ja polümorfism, kompositsiooni ja lihtsate liideste kasuks. See vähendab erandite käsitlemist tagastusväärtuste selgesõnaliste vigade kasuks. Go-koodi paigutamiseks on täpselt üks õige viis, mille on sundinud gofmt
tööriist. Ja nii edasi.
Go on ka suurepärane keel kirjutamiseks samaaegsed programmid : programmid, millel on palju iseseisvalt töötavaid osi. Ilmselge näide on veebiserver: iga taotlus töötab eraldi, kuid päringutel tuleb sageli jagada ressursse, nagu seansid, vahemälud või teatamisjärjekorrad. See tähendab osavad Go programmeerijad on vaja tegeleda samaaegse juurdepääsuga nendele ressurssidele.
Kui Golangil on samaaegsuse käsitlemiseks suurepärane madalate funktsioonide komplekt, võib nende otsene kasutamine keeruliseks muutuda. Paljudel juhtudel muudab käputäis korduvkasutatavaid abstraktsioone nende madalate mehhanismide abil elu palju lihtsamaks.
Tänases Go programmeerimise õpetuses vaatleme ühte sellist abstraktsiooni: ümbrist, mis võib muuta mis tahes andmestruktuuri tehinguteenus . Kasutame Fund
tüüp näiteks - lihtne pood meie käivitusettevõtte järelejäänud rahastamiseks, kus saame kontrollida saldot ja teha väljamakseid.
Selle praktiliseks demonstreerimiseks ehitame teenuse väikeste sammudega, tehes selle käigus segadust ja puhastades selle siis uuesti. Go õpetuse kaudu edasi liikudes näeme palju lahedaid Go keele funktsioone, sealhulgas:
Kirjutame mõne koodi, et jälgida meie käivitamise rahastamist. Fond algab antud saldoga ja raha saab välja võtta ainult (tulud selgitame välja hiljem).
Go on meelega mitte objektile orienteeritud keel: pole ühtegi klassi, objekti ega pärandit. Selle asemel kuulutame välja a struktuuri tüüp nimega Fund
, millel on lihtne funktsioon uute fondistruktuuride loomiseks ja kaks avalikku meetodit.
fond.go
package funding type Fund struct { // balance is unexported (private), because it's lowercase balance int } // A regular function returning a pointer to a fund func NewFund(initialBalance int) *Fund { // We can return a pointer to a new struct without worrying about // whether it's on the stack or heap: Go figures that out for us. return &Fund{ balance: initialBalance, } } // Methods start with a *receiver*, in this case a Fund pointer func (f *Fund) Balance() int { return f.balance } func (f *Fund) Withdraw(amount int) { f.balance -= amount }
Järgmisena vajame viisi testimiseks Fund
Eraldi programmi kirjutamise asemel kasutame Go-d testimispakett , mis pakub raamistiku nii üksustestidele kui ka võrdlusalustele. Lihtne loogika meie Fund
ei ole tegelikult väärt osakuteste kirjutamist, kuid kuna me räägime hiljem palju samaaegsest juurdepääsust fondile, on võrdlusaluse kirjutamine mõttekas.
Võrdlusuuringud on nagu ühikutestid, kuid sisaldavad tsüklit, mis käitab sama koodi mitu korda (meie puhul fund.Withdraw(1)
). See võimaldab raamistikul ajastada, kui kaua iga iteratsioon aega võtab, arvutades keskmiselt välja mööduvad erinevused kettaotsingutest, vahemälu puudumisest, protsesside ajastamisest ja muudest ettearvamatutest teguritest.
Testimisraamistik soovib, et iga võrdlusalus töötaks vähemalt ühe sekundi (vaikimisi). Selle tagamiseks kutsub see võrdlusalust mitu korda, sisestades iga kord kasvava väärtuse „iteratsioonide arv“ (väli b.N
), kuni jooks võtab vähemalt sekundi.
Praegu hoiab meie võrdlusalus raha lihtsalt hoiule ja võtab selle siis üks dollar korraga.
fund_test.go
package funding import 'testing' func BenchmarkFund(b *testing.B) { // Add as many dollars as we have iterations this run fund := NewFund(b.N) // Burn through them one at a time until they are all gone for i := 0; i Nüüd käivitame selle:
$ go test -bench . funding testing: warning: no tests to run PASS BenchmarkWithdrawals 2000000000 1.69 ns/op ok funding 3.576s
See läks hästi. Tegime kaks miljardit (!) Kordust ja lõplik saldo kontroll oli õige. Me võime ignoreerida hoiatust „Käivitada teste pole”, mis viitab üksuste testidele, mida me ei kirjutanud (hilisemates Go juhendamise näidetes selles õpetuses on hoiatus välja lõigatud).
Samaaegne juurdepääs Go-s
Nüüd muudame võrdlusuuringu samaaegseks, et modelleerida erinevaid kasutajaid, kes teevad samal ajal väljamakseid. Selleks kudeme kümme gorutiini ja laseme igal neist välja võtta kümnendiku rahast.

Goroutines on Go-keele samaaegsuse peamine ehituskivi. Nemad on rohelised niidid - kerged lõimed, mida haldab käitusaeg Go, mitte opsüsteem. See tähendab, et saate neid käitada tuhandeid (või miljoneid) ilma märkimisväärse üldkuluta. Gorutiinid sünnivad go
-ga märksõna ja alustage alati funktsiooniga (või meetodi kutsega):
// Returns immediately, without waiting for `DoSomething()` to complete go DoSomething()
Sageli soovime luua lühikese ühekordse funktsiooni vaid mõne koodireaga. Sel juhul saame funktsiooni nime asemel kasutada sulgemist:
go func() { // ... do stuff ... }() // Must be a function *call*, so remember the ()
Kui kõik meie gorutiinid on kudenud, vajame viisi nende valmimise ootamiseks. Me saaksime selle ise ehitada kanalid , kuid me pole nendega veel kokku puutunud, nii et see läheks edasi.
Praegu võime lihtsalt kasutada WaitGroup
tippige Go standardsesse teeki, mis on selleks otstarbeks olemas. Loome ühe (nimega 'wg
') ja helistame wg.Add(1)
enne iga töötaja kudemist jälgima, kui palju neid on. Siis annavad töötajad aru, kasutades wg.Done()
. Vahepeal peamises gorutiinis võime lihtsalt öelda wg.Wait()
blokeerida, kuni iga töötaja on lõpetanud.
Töötajate gorutiinide sees kasutame järgmises näites defer
helistada wg.Done()
.
defer
võtab funktsiooni (või meetodi) väljakutse ja käivitab selle vahetult enne praeguse funktsiooni naasmist, kui kõik muu on tehtud. See on puhastamiseks mugav:
func() { resource.Lock() defer resource.Unlock() // Do stuff with resource }()
Nii saame hõlpsalt sobitada Unlock
oma Lock
-ga loetavuse huvides. Veelgi olulisem on see, et käivitatakse edasi lükatud funktsioon isegi kui tekib paanika põhifunktsioonis (mida võiksime proovida teistes keeltes proovimise kaudu).
Viimasena täidavad rakenduses edasilükatud funktsioonid tagurpidi järjekorras, kuhu neid kutsuti, see tähendab, et saame pesade puhastamise kenasti teha (sarnane pesastatud goto
s ja label
s C-i idioomiga, kuid palju korralikum):
func() { db.Connect() defer db.Disconnect() // If Begin panics, only db.Disconnect() will execute transaction.Begin() defer transaction.Close() // From here on, transaction.Close() will run first, // and then db.Disconnect() // ... }()
OK, nii et koos kõige öelduga on siin uus versioon:
fund_test.go
package funding import ( 'sync' 'testing' ) const WORKERS = 10 func BenchmarkWithdrawals(b *testing.B) { // Skip N = 1 if b.N Me võime ennustada, mis siin juhtuma hakkab. Töötajad täidavad kõik Withdraw
üksteise peal. Selle sees f.balance -= amount
loeb saldo, lahutab ühe ja kirjutab selle siis tagasi. Kuid mõnikord loevad kaks või enam töötajat sama saldo ja teevad sama lahutamise ning tulemuseks on vale kogusumma. Eks?
$ go test -bench . funding BenchmarkWithdrawals 2000000000 2.01 ns/op ok funding 4.220s
Ei, see läheb ikka üle. Mis siin juhtus?
Pidage meeles, et gorutiinid on rohelised niidid - neid haldab käitusaeg Go, mitte OS. Käitusaeg ajakohastab rutiinid nii paljude OS-i lõimedega kui see on saadaval. Selle Go keeleõpetuse kirjutamise ajal ei püüa Go arvata, mitu OS-i lõime ta peaks kasutama, ja kui me tahame rohkem kui ühte, peame seda ka ütlema. Lõpuks ei eelista praegune käitusaeg gorutineid - gorutiin töötab edasi, kuni ta teeb midagi, mis viitab sellele, et see on pausiks valmis (näiteks suhtlemine kanaliga).
Kõik see tähendab, et kuigi meie võrdlusalus on nüüd samaaegne, pole see nii paralleelselt . Ainult üks meie töötajatest töötab korraga ja see kestab seni, kuni see on tehtud. Saame seda muuta, käskides Go-l kasutada rohkem lõime, GOMAXPROCS
kaudu keskkonnamuutuja.
$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 --- FAIL: BenchmarkWithdrawals-4 account_test.go:39: Balance wasn't zero: 4238 ok funding 0.007s
See on parem. Nüüd kaotame ilmselgelt osa oma väljavõtetest, nagu ootasime.

Tehke sellest server
Sel hetkel on meil erinevaid võimalusi. Võiksime fondi ümber lisada selgesõnalise muteks- või kirjutamis- ja lukustusluku. Võiksime kasutada versiooni numbriga võrdlemist ja vahetamist. Me võiksime kõik välja käia ja kasutada a CRDT skeem (võib-olla asendada väli balance
iga kliendi tehingute loenditega ja arvutada nendest saldo).
Kuid me ei hakka neid asju nüüd tegema, sest need on segased või õudsed või mõlemad. Selle asemel otsustame, et fond peaks olema server . Mis on server? See on midagi, millega räägite. Go'is räägivad asjad kanalite kaudu.
Kanalid on põhiline sidemehhanism gorutiinide vahel. Väärtused saadetakse kanalile (koos channel <- value
) ja neid saab vastu võtta teiselt poolt (koos value = <- channel
). Kanalid on gorutiinile ohutud, mis tähendab, et neile saab korraga saata ja vastu võtta mis tahes arv gorutine.
Puhverdamine
Suhtekanalite puhverdamine võib teatud tingimustel olla jõudluse optimeerimine, kuid seda tuleks kasutada väga hoolikalt (ja võrdlusuuringutega!).
Siiski on puhverdatud kanalite kasutusi, mis ei ole otseselt seotud suhtlusega.
Näiteks loob levinud gaasikeelde kanal (näiteks) puhvri suurusega „10“ ja saadab siis sinna kohe kümme märki. Seejärel kudetakse suvaline arv töövõtjate goruute ja igaüks saab enne töö alustamist kanalilt märgi ja saadab selle pärast tagasi. Siis, kui palju töötajaid on, töötab korraga korraga ainult kümme.Vaikimisi Go kanalid on puhverdamata . See tähendab, et väärtuse saatmine kanalile blokeeritakse, kuni mõni teine gorutiin on valmis seda kohe vastu võtma. Go toetab ka kanalite fikseeritud puhvri suurusi (kasutades make(chan someType, bufferSize)
). Tavaliseks kasutamiseks on see siiski nii tavaliselt halb idee .
Kujutage ette meie fondi veebiserverit, kus iga taotlus teeb väljamakse. Kui asjad on väga hõivatud, siis FundServer
ei suuda sammu pidada ja tema käsukanalile saadetavad taotlused hakkavad blokeerima ja ootama. Sel hetkel saame jõustada maksimaalse taotluste arvu serveris ja tagastada mõistliku veakoodi (näiteks 503 Service Unavailable
) klientidele, kes ületavad selle piiri. See on parim käitumine, kui server on ülekoormatud.
Puhverdamise lisamine meie kanalitesse muudaks selle käitumise vähem deterministlikuks. Võiksime hõlpsalt jõuda töötlemata käskude pikkade järjekordadeni, mis põhinevad kliendi poolt palju varem nähtud infol (ja võib-olla ka päringutel, mis olid juba enne vastuvoolu aegunud). Sama kehtib paljudes muudes olukordades, näiteks TCP-s vasturõhu rakendamisel, kui vastuvõtja ei suuda saatjaga sammu pidada.
Igal juhul jääme Go näite puhul vaikimisi puhverdamata käitumise juurde.
Kasutame kanalit käskude saatmiseks meie FundServer
Iga võrdlustöötaja saadab kanalile käsklused, kuid ainult server saab need.
Võiksime muuta oma fonditüübi otse serveri juurutamiseks, kuid see oleks segane - segaksime samaaegsuse ja äriloogika. Selle asemel jätame fondi tüübi täpselt selliseks, nagu see on, ja teeme FundServer
selle ümber eraldi ümbris.
Nagu igal serveril, on ka ümbrisel peatsilmus, kus ta ootab käske ja vastab mõlemale omakorda. Siin on veel üks detail, millele peame tähelepanu pöörama: käskude tüüp.

Näpunäited
Me oleksime võinud panna oma käskude kanali viima * osutajad * käskudele (`chan * TransactionCommand`). Miks me ei teinud?
Näidikute edastamine gorutiinide vahel on riskantne, sest kumbki gorutiin võib seda muuta. Samuti on see sageli vähem efektiivne, kuna teine gorutiin võib töötada teises protsessori tuumas (see tähendab rohkem vahemälu kehtetuks muutmist).
Kui vähegi võimalik, eelistage lihtsate väärtuste edastamist.Järgmises allpool olevas jaotises saadame mitu erinevat käsku, millel kõigil on oma struktuuritüüp. Soovime, et serveri käsukanal aktsepteeriks mõnda neist. OOP-keeles võiksime seda teha polümorfismi kaudu: laske kanalil võtta superklass, mille üksikud käsutüübid olid alamklassid. Go-s kasutame liidesed selle asemel.
Liides on meetodi allkirjade kogum. Igat tüüpi, mis rakendab kõiki neid meetodeid, saab käsitleda selle liidesena (ilma et seda oleks deklareeritud). Esimesel käivitamisel ei paljasta meie käsureklaamide abil ühtegi meetodit, seega kasutame tühja liidest interface{}
Kuna sellel pole nõudeid, mis tahes väärtus (sealhulgas primitiivsed väärtused nagu täisarvud) vastab tühjale liidesele. See ei ole ideaalne - me tahame aktsepteerida ainult käsureklaame - kuid naaseme selle juurde hiljem.
Alustame nüüd Go serveri tellingutega:
server.go
package funding type FundServer struct { Commands chan interface{} fund Fund } func NewFundServer(initialBalance int) *FundServer { server := &FundServer{ // make() creates builtins like channels, maps, and slices Commands: make(chan interface{}), fund: NewFund(initialBalance), } // Spawn off the server's main loop immediately go server.loop() return server } func (s *FundServer) loop() { // The built-in 'range' clause can iterate over channels, // amongst other things for command := range s.Commands { // Handle the command } }
Lisame nüüd käsudele paar Golangi struktuuritüüpi:
type WithdrawCommand struct { Amount int } type BalanceCommand struct { Response chan int }
WithdrawCommand
sisaldab lihtsalt väljavõetavat summat. Vastust pole. BalanceCommand
on vastus, nii et see sisaldab kanalit selle saatmiseks. See tagab, et vastused jõuavad alati õigesse kohta, isegi kui meie fond otsustab hiljem vastata korrast ära.
Nüüd saame kirjutada serveri põhilingi:
func (s *FundServer) loop() { for command := range s.Commands { // command is just an interface{}, but we can check its real type switch command.(type) { case WithdrawCommand: // And then use a 'type assertion' to convert it withdrawal := command.(WithdrawCommand) s.fund.Withdraw(withdrawal.Amount) case BalanceCommand: getBalance := command.(BalanceCommand) balance := s.fund.Balance() getBalance.Response <- balance default: panic(fmt.Sprintf('Unrecognized command: %v', command)) } } }
Hmm. See on kuidagi kole. Lülitame käsutüübi sisse, kasutame tüübikinnitusi ja võib-olla krahhi. Lähme nagunii edasi ja värskendame serveri kasutamiseks võrdlusalust.
func BenchmarkWithdrawals(b *testing.B) { // ... server := NewFundServer(b.N) // ... // Spawn off the workers for i := 0; i Ka see oli omamoodi kole, eriti kui kontrollisime tasakaalu. Ära pane tähele. Proovime seda:
$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 5000000 465 ns/op ok funding 2.822s
Palju parem, me ei kaota enam väljamakseid. Kuid kood on raskesti loetav ja on ka tõsisemaid probleeme. Kui me kunagi välja anname BalanceCommand
ja unustage siis vastust lugeda, blokeerib meie fondiserver igavesti selle saatmise. Koristagem natuke asju.
Tehke sellest teenus
Server on asi, millega räägite. Mis on teenus? Teenus on asi, millega räägite API-ga . Selle asemel, et kliendikood töötaks käsukanaliga otse, muudame kanali eksportimata (privaatseks) ja pakendame olemasolevad käsud funktsioonidesse.
type FundServer struct { commands chan interface{} // Lowercase name, unexported // ... } func (s *FundServer) Balance() int { responseChan := make(chan int) s.commands <- BalanceCommand{ Response: responseChan } return <- responseChan } func (s *FundServer) Withdraw(amount int) { s.commands <- WithdrawCommand{ Amount: amount } }
Nüüd võib meie võrdlusalus lihtsalt öelda server.Withdraw(1)
ja balance := server.Balance()
ning on vähem võimalusi kogemata kehtetute käskude saatmiseks või vastuste lugemise unustamiseks.

Käskude jaoks on veel palju lisakatla plaate, kuid selle juurde tuleme hiljem tagasi.
Tehingud
Lõpuks saab raha alati otsa. Lepime kokku, et lõpetame raha väljavõtmise, kui meie fond on jõudnud viimase kümne dollarini, ja kulutame selle raha ühispitsale, et seda tähistada või kokku panna. Meie võrdlusalus kajastab seda:
// Spawn off the workers for i := 0; i Seekord saame tõesti tulemust ennustada.
$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 --- FAIL: BenchmarkWithdrawals-4 fund_test.go:43: Balance wasn't ten dollars: 6 ok funding 0.009s
Oleme tagasi seal, kus alustasime - mitu töötajat saavad bilanssi korraga lugeda ja siis kõik seda värskendada. Sellega tegelemiseks võiksime lisada fondi enda sisse mõne loogika, näiteks minimumBalance
või lisage veel üks käsk nimega WithdrawIfOverXDollars
. Need on mõlemad kohutavad ideed. Meie leping on meie endi vahel, mitte fondi omand. Peaksime seda rakendusloogikas hoidma.
Mida me tegelikult vajame, on tehingud , samas tähenduses nagu andmebaasitehingud. Kuna meie teenus täidab korraga ainult ühte käsku, on see ülilihtne. Lisame Transact
käsk, mis sisaldab tagasihelistamist (sulgemist). Server täidab selle tagasihelistamise oma enda rutiinis, edastades toore Fund
Tagasihelistamine võib seejärel Fund
abil teha turvaliselt kõike, mis talle meeldib.
Semafoorid ja vead
Selles järgmises näites teeme kaks väikest asja valesti.
Esiteks kasutame semaforina kanalit „Valmis“, et helistamiskoodile teada anda, kui selle tehing on lõppenud. See on tore, aga miks on kanalitüüp `bool`? Saadame sellesse ainult tõelise tähenduse, mis tähendab 'valmis' (mida tähendaks 'vale' saatmine isegi?). Mida me tegelikult tahame, on ühe riigi väärtus (väärtus, millel pole väärtust?). Go-s saame seda teha, kasutades tühja struktuuri tüüpi: `struct {}`. Selle eeliseks on ka vähem mälu. Selles näites jääme 'bool' juurde, et mitte liiga hirmutav välja näha.
Teiseks ei anna meie tehingu tagasihelistamine midagi tagasi. Nagu näeme hetkega, saame helistamiskoodi abil tagasihelistamise väärtused ulatustrikkide abil. Tõenäoliselt ebaõnnestuvad tehingud reaalses süsteemis mõnikord, nii et Go-konventsiooni kohaselt peaks tehing tagastama 'vea' (ja seejärel kontrollima, kas see ei olnud helistamiskoodis 'null').
Me ei tee seda ka praegu, kuna meil pole genereerimiseks ühtegi viga. // Typedef the callback for readability type Transactor func(fund *Fund) // Add a new command type with a callback and a semaphore channel type TransactionCommand struct { Transactor Transactor Done chan bool } // ... // Wrap it up neatly in an API method, like the other commands func (s *FundServer) Transact(transactor Transactor) { command := TransactionCommand{ Transactor: transactor, Done: make(chan bool), } s.commands <- command <- command.Done } // ... func (s *FundServer) loop() { for command := range s.commands { switch command.(type) { // ... case TransactionCommand: transaction := command.(TransactionCommand) transaction.Transactor(s.fund) transaction.Done <- true // ... } } }
Meie tehingute tagasihelistamised ei too otseselt midagi tagasi, kuid Go keele abil on väärtusi otse sulgemisest välja saada, nii et teeme seda võrdlusalusena pizzaTime
lipp, kui raha on vähe:
pizzaTime := false for i := 0; i Ja kontrollige, kas see töötab:
$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 5000000 775 ns/op ok funding 4.637s
Ei midagi peale tehingute
Võimalik, et olete nüüd märganud võimalust asju veel koristada. Kuna meil on üldine Transact
käsku, me ei vaja WithdrawCommand
või BalanceCommand
enam. Kirjutame need tehingute osas ümber:
func (s *FundServer) Balance() int { var balance int s.Transact(func(f *Fund) { balance = f.Balance() }) return balance } func (s *FundServer) Withdraw(amount int) { s.Transact(func (f *Fund) { f.Withdraw(amount) }) }
Ainus käsk, mille server võtab, on TransactionCommand
, nii et saame kogu interface{}
eemaldada jama selle rakendamisel ja kas see aktsepteerib ainult tehingukäske:
type FundServer struct { commands chan TransactionCommand fund *Fund } func (s *FundServer) loop() { for transaction := range s.commands { // Now we don't need any type-switch mess transaction.Transactor(s.fund) transaction.Done <- true } }
Palju parem.
Siin on viimane samm. Lisaks mugavuse funktsioonidele Balance
ja Withdraw
, teenuse juurutamine pole enam seotud Fund
-ga. Fund
Haldamise asemel võiks see hallata ka interface{}
ja seda tuleks kasutada mähkimiseks midagi . Kuid iga tehingu tagasihelistamine peaks siis teisendama interface{}
tagasi tegeliku väärtuse juurde:
type Transactor func(interface{}) server.Transact(func(managedValue interface{}) { fund := managedValue.(*Fund) // Do stuff with fund ... })
See on kole ja veaohtlik. Mida me tegelikult tahame, on kompileerimisaegsed geneerilised ained, nii et saame teatud tüüpi serveri (näiteks *Fund
) jaoks malli välja mallida.
Kahjuks ei toeta Go veel geneerilisi ravimeid. Eeldatakse, et see saabub lõpuks, kui keegi saab selle jaoks mõistliku süntaksi ja semantika. Vahepeal eemaldab liidese hoolikas kujundamine vajaduse geneeriliste ravimite järele ja kui seda pole, saame tüübikinnitustega (mida kontrollitakse käitamise ajal) hakkama.
Kas oleme valmis?
Jah.
Noh, okei, ei.
Näiteks:
-
Tehingu paanika tapab kogu teenuse.
-
Ajastusi pole. Tehing, mis kunagi tagasi ei tule, blokeerib teenuse igaveseks.
-
Kui meie fond kasvatab mõnda uut välja ja mõni tehing kukub nende värskendamise keskel kokku, on meil olukord vastuoluline.
-
Tehingud suudavad hallatud Fund
lekitada objekt, mis pole hea.
-
Mitme fondiga tehinguid pole mõistlik teha (nt ühelt välja võtta ja teise hoiustada). Me ei saa oma tehinguid lihtsalt pesitseda, sest see võimaldaks ummikuid.
-
Tehingu asünkroonne juhtimine nõuab nüüd uut gorutiini ja palju jamamist. Seoses sellega tahame ilmselt lugeda viimast Fund
ajal, mil kestab kauaaegne tehing.
Järgmises Go programmeerimiskeele õpetuses uurime mõningaid viise nende probleemide lahendamiseks.
Seotud: Hästi struktureeritud loogika: Golangi OOP-õpetus Põhitõdede mõistmine
Mis keeles on Go keel kirjutatud?
Go programmeerimiskeele spetsifikatsioon on inglise keeles kirjutatud dokument, Go standardne teek ja kompilaator aga Go-s.
Milleks Go kasutatakse?
Go on üldotstarbeline programmeerimiskeel ja seda saab kasutada mitmesugustel eesmärkidel. Go-d saab tagaosas kasutada veebiserverina ning seda on kasutatud Dockeri, Kubernetese ja Heroku CLI loomiseks.
Mis ettevõtted Go-d kasutavad?
Go-d kasutavad paljud mainekad tehnoloogiaettevõtted, eriti Google, Dropbox, Docker, Kubernetes, Heroku ja paljud teised.