socialgekon.com
  • Tärkein
  • Varastointi
  • Ihmiset Ja Joukkueet
  • Ongelmien karttoittaminen
  • Sijoittajat Ja Rahoitus
Teknologia

Mene ohjelmointikieli: Johdanto Golang-opetusohjelma

Mikä on Go-ohjelmointikieli?

Suhteellisen uusi Mene ohjelmointikielelle istuu siististi keskellä maisemaa tarjoten paljon hyviä ominaisuuksia ja jättämällä tarkoituksella pois monia huonoja. Se kääntyy nopeasti, toimii nopeasti, sisältää ajonaikaisen ja roskakorin, sillä on yksinkertainen staattisen tyyppinen järjestelmä ja dynaamiset käyttöliittymät sekä erinomainen vakiokirjasto. Siksi niin monet kehittäjät haluavat oppia Go-ohjelmoinnin.

Golang-opetusohjelma: logokuvitus

Mene ja OOP
OOP on yksi niistä ominaisuuksista, jotka Go tahallaan jättää pois. Sillä ei ole alaluokitusta, joten ei ole olemassa perintötimantteja, superpuheluja tai virtuaalisia menetelmiä, jotka vievät sinut ylös. Silti monia OOP: n hyödyllisiä osia on saatavana muilla tavoin.

* Mixins * on saatavana upottamalla rakenteita nimettömästi, jolloin niiden menetelmiä voidaan kutsua suoraan sisältävään strukturiin (katso upottaminen ). Menetelmien mainostamista tällä tavalla kutsutaan * edelleenlähetykseksi *, ja se ei ole sama kuin aliluokittelu: menetelmää käytetään edelleen sisäisessä, upotetussa rakenteessa.

Upottaminen ei myöskään tarkoita polymorfismia. Vaikka `A` voiomistaaa `B`, se ei tarkoita sitäOna `B` - toiminnot, jotka ottavat` `B '', eivät ota` `A '': ta. Tätä varten tarvitsemmerajapinnat, jonka kohtaamme lyhyesti myöhemmin.

Sillä välin Golangilla on vahva kanta ominaisuuksiin, jotka voivat johtaa sekaannukseen ja virheisiin. Siinä jätetään pois OOP-idiomit, kuten perintö ja polymorfismi, koostumuksen ja yksinkertaisten rajapintojen hyväksi. Se vähentää poikkeusten käsittelyä paluuarvoissa olevien nimenomaisten virheiden hyväksi. Go-koodin asettamiseen on täsmälleen yksi oikea tapa, jonka gofmt pakottaa työkalu. Ja niin edelleen.



Miksi oppia Golangia?

Go on myös hyvä kieli kirjoittamiseen samanaikaisia ​​ohjelmia : ohjelmat, joissa on paljon itsenäisesti käynnissä olevia osia. Ilmeinen esimerkki on verkkopalvelin: Jokainen pyyntö suoritetaan erikseen, mutta pyyntöjen on usein jaettava resursseja, kuten istuntoja, välimuistit tai ilmoitusjonot. Tämä tarkoittaa ammattitaitoiset Go-ohjelmoijat on käsiteltävä samanaikaista pääsyä kyseisiin resursseihin.

Vaikka Golangilla on erinomainen joukko matalan tason ominaisuuksia samanaikaisuuden käsittelemiseksi, niiden suora käyttö voi olla monimutkaista. Monissa tapauksissa kourallinen uudelleenkäytettäviä abstraktioita noilla matalan tason mekanismeilla tekee elämästä paljon helpompaa.

Tämänpäiväisessä Go-ohjelmointioppaassa tarkastellaan yhtä tällaista abstraktiota: Kääre, joka voi muuttaa minkä tahansa tietorakenteen kaupallinen palvelu . Käytämme Fund tyyppi esimerkkinä - yksinkertainen myymälä käynnistysyrityksen jäljellä olevalle rahoitukselle, jossa voimme tarkistaa saldon ja tehdä nostoja.

Osoittaaksemme tämän käytännössä rakennamme palvelun pienin askelin, tekemällä sotkua matkan varrella ja siivoamalla sen sitten uudelleen. Go-opetusohjelmamme edetessä kohtaamme monia hienoja Go-kieliominaisuuksia, kuten:

  • Rakennetyypit ja menetelmät
  • Yksikkötestit ja vertailuarvot
  • Goroutines ja kanavat
  • Liitännät ja dynaaminen kirjoittaminen

Yksinkertaisen rahaston rakentaminen

Kirjoitetaan koodi seuratakseen startupin rahoitusta. Rahasto alkaa tietyllä saldolla, ja rahaa voidaan nostaa vain (tulot selvitetään myöhemmin).

Tämä kuva kuvaa yksinkertaisen gorutiiniesimerkin Go-ohjelmointikielellä.

Go on tarkoituksella ei olio-orientoitu kieli: Ei ole luokkia, objekteja tai perintöä. Sen sijaan ilmoitamme a rakennetyyppi kutsutaan Fund, jolla on yksinkertainen toiminto uusien rahastorakenteiden luomiseen, ja kahdella julkisella menetelmällä.

fund.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 }

Testaus vertailuarvoilla

Seuraavaksi tarvitaan tapa testata Fund Sen sijaan, että kirjoittaisimme erillisen ohjelman, käytämme Go-ohjelmaa testauspaketti , joka tarjoaa puitteet sekä yksikkötesteille että vertailuarvoille. Yksinkertainen logiikka Fund -sivullamme ei todellakaan kannata kirjoittaa osuustestit, mutta koska puhumme paljon samanaikaisesta pääsystä rahastoon myöhemmin, vertailuarvon kirjoittaminen on järkevää.

Vertailuarvot ovat kuin yksikötestit, mutta sisältävät silmukan, joka suorittaa saman koodin monta kertaa (meidän tapauksessamme fund.Withdraw(1)). Tämä antaa kehykselle ajan, kuinka kauan kukin iterointi kestää, keskiarvoistamalla ohimenevät erot levynhakemuksista, välimuistihäiriöistä, prosessin ajoituksista ja muista arvaamattomista tekijöistä.

Testauskehys haluaa, että jokainen vertailuarvo toimii vähintään yhden sekunnin ajan (oletusarvoisesti). Tämän varmistamiseksi se kutsuu vertailuarvoa useita kertoja, välittäen joka kerta kasvavan määrän iteraatioita (b.N -kenttä), kunnes ajo kestää vähintään sekunnin.

Toistaiseksi vertailuarvomme vain tallettaa rahaa ja nostaa sen sitten dollarin kerrallaan.

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

Suoritetaan nyt se:

$ go test -bench . funding testing: warning: no tests to run PASS BenchmarkWithdrawals 2000000000 1.69 ns/op ok funding 3.576s

Se meni hyvin. Suoritimme kaksi miljardia (!) Toistoa, ja saldon viimeinen tarkistus oli oikea. Voimme jättää huomiotta 'ei testejä suoritettavaksi' -varoituksen, joka viittaa yksikkötesteihin, joita emme kirjoittaneet (tämän opetusohjelman myöhemmissä Go-ohjelmointiesimerkeissä varoitus on katkaistu).

Samanaikainen käyttö Go-palvelussa

Tehdään nyt vertailuarvo samanaikaisesti mallinnamaan eri käyttäjiä, jotka tekevät nostoja samanaikaisesti. Tätä varten kutemme kymmenen gorutiinia ja annamme jokaiselle niistä kymmenesosan rahaa.

Kuinka rakennamme useita samanaikaisia ​​gorutineja Go-kielellä?

Goroutiinit ovat Go-kielen samanaikaisuuden peruselementti. He ovat vihreät langat - kevyet langat, joita hallinnoi Go-ajoaika, ei käyttöjärjestelmä. Tämä tarkoittaa, että voit suorittaa tuhansia (tai miljoonia) niitä ilman merkittäviä yleiskustannuksia. Goroutiineille syntyy go avainsana ja aloita aina funktiolla (tai menetelmäpuhelulla):

// Returns immediately, without waiting for `DoSomething()` to complete go DoSomething()

Usein haluamme luoda lyhyen kertatoiminnon vain muutamalla rivillä koodia. Tässä tapauksessa voimme käyttää sulkua funktion nimen sijasta:

go func() { // ... do stuff ... }() // Must be a function *call*, so remember the ()

Kun kaikki gorutiinimme ovat syntyneet, tarvitsemme tapaa odottaa niiden päättymistä. Voisimme rakentaa sellaisen itse kanavia , mutta emme ole vielä kohdanneet niitä, joten se hyppää eteenpäin.

Toistaiseksi voimme käyttää vain WaitGroup kirjoita Go: n vakiokirjastoon, joka on olemassa juuri tähän tarkoitukseen. Luomme sellaisen (nimeltään 'wg') ja soitamme wg.Add(1) ennen kutemaan kutakin työntekijää seuraamaan, kuinka monta työntekijää on. Sitten työntekijät raportoivat takaisin käyttämällä wg.Done(). Sillä välin päägorutiinissa voimme vain sanoa wg.Wait() estää, kunnes jokainen työntekijä on valmis.

Seuraavassa esimerkissä työntekijän gorutiinien sisällä käytämme defer soittaa wg.Done().

defer ottaa toiminnon (tai menetelmän) kutsun ja suorittaa sen välittömästi ennen nykyisen funktion paluuta, kun kaikki muu on tehty. Tämä on kätevä puhdistamiseen:

func() { resource.Lock() defer resource.Unlock() // Do stuff with resource }()

Tällä tavalla voimme helposti sovittaa Unlock sen Lock -sisällöllä luettavuuden takaamiseksi. Vielä tärkeämpää on, että lykätty toiminto suoritetaan vaikka vallitsisi paniikki päätoiminnossa (jotain, jonka voimme käsitellä kokeilemalla lopulta muilla kielillä).

Lopuksi, lykätyt toiminnot suoritetaan käänteinen järjestys, johon heidät kutsuttiin, mikä tarkoittaa, että voimme tehdä sisäkkäisen siivouksen hienosti (samanlainen kuin sisäkkäisten goto s ja label s, mutta paljon siistimpi C-idioomi):

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, joten kaikesta sanotusta tässä on uusi versio:

fund_test.go

package funding import ( 'sync' 'testing' ) const WORKERS = 10 func BenchmarkWithdrawals(b *testing.B) { // Skip N = 1 if b.N

Voimme ennustaa, mitä täällä tapahtuu. Työntekijät kaikki teloittavat Withdraw päällekkäin. Sen sisällä f.balance -= amount lukee saldon, vähentää yhden ja kirjoittaa sen sitten takaisin. Mutta joskus kaksi tai useampia työntekijöitä molemmat lukevat saman tasapainon ja tekevät saman vähennyslaskun, ja lopputulos on väärä. Eikö?

$ go test -bench . funding BenchmarkWithdrawals 2000000000 2.01 ns/op ok funding 4.220s

Ei, se silti kulkee. Mitä täällä tapahtui?

Muista, että gorutiinit ovat vihreät langat - niitä hallinnoi Go-ajoaika, ei käyttöjärjestelmä. Ajonaikainen aikataulu laskee gorutiinit kaikilla käytettävissä olevilla käyttöjärjestelmäketjuilla. Tämän Go-kielen opetusohjelman kirjoittamisen aikana Go ei yritä arvata, kuinka monta käyttöjärjestelmän ketjua sen pitäisi käyttää, ja jos haluamme enemmän kuin yhden, meidän on sanottava niin. Lopuksi, nykyinen ajonaika ei estä gorutineja - gorutine jatkuu, kunnes se tekee jotain, mikä viittaa siihen, että se on valmis taukolle (kuten vuorovaikutuksessa kanavan kanssa).

Kaikki tämä tarkoittaa, että vaikka vertailuarvomme on nyt samanaikainen, se ei ole rinnakkain . Vain yksi työntekijöistämme juoksee kerrallaan, ja se toimii, kunnes se on valmis. Voimme muuttaa tätä kertomalla Go: lle käyttämään lisää ketjuja GOMAXPROCS: n kautta ympäristömuuttuja.

$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 --- FAIL: BenchmarkWithdrawals-4 account_test.go:39: Balance wasn't zero: 4238 ok funding 0.007s

Tuo on parempi. Menetämme nyt ilmeisesti joitain nostojamme, kuten odotimme.

Tässä Go-ohjelmointiesimerkissä useiden rinnakkaisten gorutiinien tulos ei ole suotuisa.

Tee siitä palvelin

Tässä vaiheessa meillä on useita vaihtoehtoja. Voisimme lisätä nimenomaisen mutex- tai luku- ja kirjoituslukon rahaston ympärille. Voisimme käyttää vertailua ja vaihtamista versionumeron kanssa. Voisimme mennä kaikki ulos ja käyttää a CRDT (ehkä korvaamalla balance -kenttä kunkin asiakkaan tapahtumaluetteloilla ja laskemalla saldo niistä).

Mutta emme tee mitään näistä asioista nyt, koska ne ovat sotkuisia tai pelottavia tai molempia. Sen sijaan päätämme, että rahaston tulisi olla palvelin . Mikä palvelin on? Se on jotain mitä puhut. Go: ssa asiat puhuvat kanavien kautta.

Kanavat ovat perusviestintämekanismi gorutiinien välillä. Arvot lähetetään kanavalle (painamalla channel <- value), ja ne voidaan vastaanottaa toiselle puolelle (value = <- channel: lla). Kanavat ovat 'gorutine-turvallisia', mikä tarkoittaa, että mikä tahansa määrä gorutineja voi lähettää ja vastaanottaa heitä samanaikaisesti.

Puskurointi
Puskurointiviestintäkanavat voivat olla suorituskyvyn optimointi tietyissä olosuhteissa, mutta sitä tulisi käyttää erittäin huolellisesti (ja vertailemalla!).

Puskuroiduille kanaville on kuitenkin käyttötarkoituksia, jotka eivät liity suoraan viestintään.

Esimerkiksi yleinen kuristus-idiooma luo kanavan, jolla on (esimerkiksi) puskurikoko '10', ja lähettää sitten siihen välittömästi kymmenen tunnusta. Mikä tahansa määrä työntekijän gorutineja syntyy sitten, ja kukin saa kanavalta tunnuksen ennen työn aloittamista ja lähettää sen jälkeenpäin. Sitten, vaikka kuinka monta työntekijää onkin, vain kymmenen työskentelee koskaan samanaikaisesti.

Oletuksena Go-kanavat ovat puskuroimaton . Tämä tarkoittaa, että arvon lähettäminen kanavalle estää, kunnes toinen gorutiini on valmis vastaanottamaan sen välittömästi. Go tukee myös kanavien kiinteitä puskurikokoja (käyttämällä make(chan someType, bufferSize)). Normaalikäytössä tämä on kuitenkin yleensä huono idea .

Kuvittele rahastomme verkkopalvelin, jossa jokainen pyyntö tekee noston. Kun asiat ovat hyvin kiireisiä, FundServer ei pysty pysymään mukana, ja komentokanavalle lähettämistä pyytävät pyynnöt alkavat estää ja odottaa. Siinä vaiheessa voimme pakottaa enimmäispyyntöjen määrän palvelimelle ja palauttaa järkevän virhekoodin (kuten 503 Service Unavailable) asiakkaille, jotka ylittävät tämän rajan. Tämä on paras mahdollinen käyttäytyminen, kun palvelin on ylikuormitettu.

Puskurin lisääminen kanavillemme tekisi tästä käyttäytymisestä vähemmän determinististä. Voisimme helposti päätyä pitkiin jonoihin käsittelemättömiä komentoja, jotka perustuvat asiakkaan paljon aikaisemmin näkemiin tietoihin (ja ehkä pyyntöihin, jotka olivat sittemmin aikakatkaistut ylävirtaan). Sama pätee moniin muihin tilanteisiin, kuten vastapaineen asettamiseen TCP: n kautta, kun vastaanotin ei pysty pysymään lähettäjän mukana.

Joka tapauksessa Go-esimerkissämme pidämme kiinni puskuroimattomasta oletuskäyttäytymisestä.

Lähetämme komentoja kanavallamme FundServer Jokainen vertailutyöntekijä lähettää komennot kanavalle, mutta vain palvelin saa ne.

Voisimme muuttaa rahastotyypistämme suoraan palvelintoteutuksen, mutta se olisi sotkuista - sekoittaisimme samanaikaisuuden käsittelyä ja liiketoimintalogiikkaa. Sen sijaan jätämme rahastotyypin sellaisenaan ja teemme FundServer erillinen kääre sen ympärillä.

Kuten kaikilla palvelimilla, käärimellä on pääsilmukka, jossa se odottaa komentoja ja vastaa kuhunkin vuorotellen. Tässä on vielä yksi yksityiskohta, johon meidän on puututtava: komentojen tyyppi.

Kaavio rahastosta, jota käytetään palvelimena tässä Go-ohjelmointioppaassa.

Osoittimet
Olisimme voineet saada komentokanavamme ottamaan * osoittimia * komentoihin (`chan * TransactionCommand`). Miksi emme?

Osoitteiden välittäminen gorutiinien välillä on riskialtista, koska jompikumpi gorutiini saattaa muuttaa sitä. Se on myös usein vähemmän tehokas, koska toinen gorutiini saattaa olla käynnissä eri CPU-ytimellä (mikä tarkoittaa enemmän välimuistin mitätöimistä).

Aina kun mahdollista, välitä mieluummin tavalliset arvot.

Seuraavassa alla olevassa osiossa lähetämme useita erilaisia ​​komentoja, joilla kaikilla on oma rakennetyyppinsä. Haluamme, että palvelimen komentokanava hyväksyy kaikki niistä. OOP-kielellä voimme tehdä tämän polymorfismin kautta: Pyydä kanavaa ottamaan superluokka, jonka yksittäiset komentotyypit olivat alaluokkia. Go: ssa käytämme rajapinnat sen sijaan.

Käyttöliittymä on joukko menetelmäallekirjoituksia. Kaikkia tyyppejä, jotka toteuttavat kaikki nämä menetelmät, voidaan käsitellä kyseisenä rajapintana (ilmoittamatta tekemään niin). Ensimmäisessä ajossa komentorakenteemme eivät todellakaan paljasta mitään menetelmiä, joten aiomme käyttää tyhjää käyttöliittymää interface{} Koska sillä ei ole vaatimuksia, minkä tahansa arvo (mukaan lukien primitiiviset arvot, kuten kokonaisluvut) täyttää tyhjän käyttöliittymän. Tämä ei ole ihanteellinen - haluamme hyväksyä vain komentorakenteet - mutta palaamme siihen myöhemmin.

Aloitetaan toistaiseksi Go-palvelimen telineillä:

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 } }

Lisätään nyt komentoihin muutama Golang-rakennetyyppi:

type WithdrawCommand struct { Amount int } type BalanceCommand struct { Response chan int }

WithdrawCommand sisältää vain nostettavan summan. Ei vastausta. BalanceCommand ei ole vastausta, joten se sisältää kanavan sen lähettämistä varten. Tämä varmistaa, että vastaukset menevät aina oikeaan paikkaan, vaikka rahastomme myöhemmin päättäisi vastata epäkuntoon.

Nyt voimme kirjoittaa palvelimen pääsilmukan:

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. Se on tavallaan ruma. Käynnistämme komentotyypin, käytämme tyyppiväitteitä ja mahdollisesti kaatumme. Edistetään joka tapauksessa ja päivitetään vertailuarvo palvelimen käyttöä varten.

func BenchmarkWithdrawals(b *testing.B) { // ... server := NewFundServer(b.N) // ... // Spawn off the workers for i := 0; i

Se oli eräänlainen ruma, varsinkin kun tarkistimme tasapainon. Unohda koko juttu. Kokeillaan:

$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 5000000 465 ns/op ok funding 2.822s

Paljon parempi, emme enää menetä nostoja. Mutta koodia on vaikea lukea, ja on vakavampia ongelmia. Jos koskaan annamme BalanceCommand ja unohda sitten lukea vastaus, rahastopalvelimemme estää ikuisesti yrittää lähettää sitä. Siivotaan asioita vähän.

Tee siitä palvelu

Palvelin on asia, jonka kanssa puhut. Mikä palvelu on? Palvelu on jotain, jonka kanssa puhut API: lla . Sen sijaan, että asiakaskoodi toimisi suoraan komentokanavan kanssa, teemme kanavasta viemättömän (yksityisen) ja kääritään käytettävissä olevat komennot toimintoihin.

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 } }

Nyt vertailuarvomme voi vain sanoa server.Withdraw(1) ja balance := server.Balance(), ja on vähemmän mahdollisuuksia vahingossa lähettää virheellisiä komentoja tai unohtaa lukea vastauksia.

Rahaston käyttö palveluna saattaa näyttää tältä tässä Go-esimerkkiohjelmassa.

Komennoille on vielä paljon ylimääräistä kattilaa, mutta palaamme siihen myöhemmin.

Tapahtumat

Lopulta rahat loppuvat aina. Sovitaan, että lopetamme nostamisen, kun rahastomme on laskenut viimeisen kymmenen dollarinsa, ja käytämme rahat yhteispizzaan juhlimaan tai nauttimaan. Vertailuarvomme heijastaa tätä:

// Spawn off the workers for i := 0; i

Tällä kertaa voimme todella ennustaa tuloksen.

$ 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

Olemme palanneet alkuun - useat työntekijät voivat lukea tasapainon kerralla ja sitten kaikki päivittää sen. Tämän käsittelemiseksi voisimme lisätä logiikkaa itse rahastoon, kuten minimumBalance tai lisää toinen komento nimeltä WithdrawIfOverXDollars. Nämä ovat molemmat kauheita ideoita. Sopimuksemme on keskuudessamme, ei rahaston omaisuus. Meidän tulisi pitää se sovelluslogiikassa.

Tarvitsemme todella liiketoimia , samassa mielessä kuin tietokantatapahtumat. Koska palvelumme suorittaa vain yhden komennon kerrallaan, tämä on erittäin helppoa. Lisätään Transact komento, joka sisältää soittopyynnön (sulkeminen). Palvelin suorittaa kyseisen soittopyynnön oman ohjelmansa sisällä ja välittää raakan Fund Soittopyyntö voi sitten tehdä turvallisesti mitä haluaa Fund

Semaforeja ja virheitä
Tässä seuraavassa esimerkissä teemme kaksi pientä asiaa väärin.

Ensinnäkin käytämme `Done`-kanavaa semaforina, jotta puhelukoodi tiedetään, kun sen tapahtuma on päättynyt. Se on hieno, mutta miksi kanavatyyppi on 'bool'? Lähetämme siihen vain 'totta' tarkoittamaan 'valmis' (mitä 'väärän' lähettäminen edes tarkoittaisi?). Haluamme todella yhden valtion arvon (arvon, jolla ei ole arvoa?). Go-tilassa voimme tehdä tämän käyttämällä tyhjää rakennetyyppiä: `struct {}`. Tämän etuna on myös vähemmän muistia. Tässä esimerkissä pidämme kiinni `boolista ', jotta emme näytä liian pelottavalta.

Toiseksi tapahtumamme takaisinsoitto ei palauta mitään. Kuten näemme hetkessä, voimme saada arvot takaisinsoittosta soittokoodiksi soveltamisalueen temppujen avulla. Todellisessa järjestelmässä tapahtuvat tapahtumat todennäköisesti epäonnistuvat joskus, joten Go-käytännön mukaan tapahtuma palauttaisi 'virheen' (ja tarkista sitten, onko se 'tyhjä' kutsukoodissa).

Emme tee sitä myöskään toistaiseksi, koska meillä ei ole virheitä. // 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 // ... } } }

Tapahtumien takaisinkutsut eivät palauta suoraan mitään, mutta Go-kielellä on helppo saada arvoja suoraan sulkemisesta, joten teemme sen vertailuarvossa asettaaksesi pizzaTime lippu, kun rahaa loppuu:

pizzaTime := false for i := 0; i

Ja tarkista, että se toimii:

$ GOMAXPROCS=4 go test -bench . funding BenchmarkWithdrawals-4 5000000 775 ns/op ok funding 4.637s

Ei muuta kuin liiketoimet

Olet ehkä havainnut tilaisuuden puhdistaa asioita vielä lisää. Koska meillä on yleinen Transact komentoa, emme tarvitse WithdrawCommand tai BalanceCommand enää. Kirjoitamme ne uudelleen tapahtumien muodossa:

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) }) }

Ainoa komento, jonka palvelin ottaa, on TransactionCommand, joten voimme poistaa koko interface{} sotku sen toteutuksessa, ja pyydä sitä hyväksymään vain tapahtumakomennot:

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 } }

Paljon parempi.

Tässä on viimeinen askel. Sen lisäksi, että se tarjoaa Balance ja Withdraw, palvelun toteutus ei ole enää sidottu Fund Sen sijaan, että hallinnoi Fund, se voisi hallita interface{} ja käytetään käärimiseen mitä tahansa . Jokaisen tapahtuman soittopyynnön tulisi kuitenkin muuntaa interface{} takaisin todelliseen arvoon:

type Transactor func(interface{}) server.Transact(func(managedValue interface{}) { fund := managedValue.(*Fund) // Do stuff with fund ... })

Tämä on rumaa ja altis virheille. Mitä todella haluamme, on kääntöaikaiset geneeriset aineet, joten voimme 'mallia' palvelimen tietylle tyypille (kuten *Fund).

Valitettavasti Go ei tue geneerisiä valmisteita - vielä. Sen odotetaan saapuvan lopulta, kun joku selvittää järkevän syntaksin ja semantiikan sille. Sillä välin huolellinen käyttöliittymäsuunnittelu poistaa usein geneeristen lääkkeiden tarpeen, ja kun niitä ei ole, voimme tulla toimeen tyyppiväitteillä (jotka tarkistetaan ajon aikana).

Olemmeko valmiita?

Joo.

No, okei, ei.

Esimerkiksi:

  • Kaupan paniikki tappaa koko palvelun.

  • Aikakatkaisuja ei ole. Tapahtuma, joka ei koskaan palaa, estää palvelun ikuisesti.

  • Jos rahastomme kasvattaa uusia kenttiä ja tapahtuma kaatuu niiden päivityksen puolivälissä, tilamme on epäjohdonmukainen.

  • Tapahtumat voivat vuotaa hallitun Fund esine, joka ei ole hyvä.

  • Ei ole mitään kohtuullista tapaa suorittaa transaktioita useilla rahastoilla (kuten nostaa yhdestä ja tallettaa toiseen). Emme voi vain sijoittaa tapahtumiamme, koska se sallisi umpikujat.

  • Tapahtuman suorittaminen asynkronisesti vaatii nyt uuden ohjelmiston ja paljon sotkua. Tähän liittyen haluamme todennäköisesti pystyä lukemaan uusimman Fund valtio muualta, kun pitkäaikainen tapahtuma on käynnissä.

Seuraavassa Go-ohjelmointikielen opetusohjelmassa tarkastelemme joitain tapoja ratkaista nämä ongelmat.

Liittyvät: Hyvin jäsennelty logiikka: Golang OOP -opetusohjelma

Perustietojen ymmärtäminen

Mikä on Go-kieli kirjoitettu?

Go-ohjelmointikielimääritys on englanniksi kirjoitettu asiakirja, kun taas Go: n vakiokirjasto ja kääntäjä on kirjoitettu itse Go-tiedostoon.

Mihin Goia käytetään?

Go on yleiskäyttöinen ohjelmointikieli ja sitä voidaan käyttää useisiin käyttötarkoituksiin. Go-sovellusta voidaan käyttää verkkopalvelimena taustapäässä, ja sitä on käytetty Dockerin, Kubernetesin ja Heroku-käyttöliittymän rakentamiseen.

Mitkä yritykset käyttävät Goia?

Go-palvelua käyttävät monet arvostetut teknologiayritykset, erityisesti Google, Dropbox, Docker, Kubernetes, Heroku ja monet muut.

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla

Taustaa

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla
10 UX-toimitettavaa huippusuunnittelijaa käyttää

10 UX-toimitettavaa huippusuunnittelijaa käyttää

Ux-Suunnittelu

Suosittu Viestiä
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
3D-grafiikka: WebGL-opetusohjelma
3D-grafiikka: WebGL-opetusohjelma
Kulttuurienvälinen suunnittelu ja UX: n rooli
Kulttuurienvälinen suunnittelu ja UX: n rooli
 
Yrityksen lähtökohta: datatiede ja tekoäly
Yrityksen lähtökohta: datatiede ja tekoäly
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Pythonin monisäikeinen ja moniprosessointiopetus
Pythonin monisäikeinen ja moniprosessointiopetus
Luokat
Tuote-Ihmiset Ja JoukkueetSuunnittelijan ElämäUi-SuunnitteluTaustaaKaukosäätimen NousuAmmuntaiOS-vinkkejäVarastointiMuuLiikevaihdon Kasvu

© 2023 | Kaikki Oikeudet Pidätetään

socialgekon.com