2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Sisällysluettelo
1. Ero merkin ja uuden välillä
2. Miten arvonsiirtoa ja osoitteensiirtoa (viitesiirtoa) käytetään Go-kielellä? Mitä eroa?
4. Mikä Go Convey on? Mihin sitä yleensä käytetään?
5. Mitkä ovat lykkäyksen toiminnot ja ominaisuudet?
6. Go slice:n taustalla oleva toteutus? Go slice laajennusmekanismi?
7. Ovatko viipaleet samat ennen ja jälkeen laajentamisen?
8. Miksi slice ei ole lankaturvallinen?
Erot: 1. Make-toimintoa voidaan käyttää vain slice-, map- ja chan-tyyppisten tietojen allokointiin ja alustamiseen, kun taas uusi voi allokoida minkä tahansa tyyppisiä tietoja.
2. Uusi allokaatio palauttaa osoittimen, jonka tyyppi on "*Tyyppi", kun taas make palauttaa viitteen, joka on Tyyppi.
3. Uuden varaama tila tyhjennetään, kun make varaa tilan, se alustetaan.
Arvon ohittaminen vain kopioi parametrin arvon ja sijoittaa sen vastaavaan funktioon. Näiden kahden muuttujan osoitteet ovat erilaisia, eivätkä ne voi muokata toisiaan.
Osoitteen välitys (lähetys viittauksella) välittää itse muuttujan vastaavaan funktioon ja muuttujan arvosisältöä voidaan muuttaa funktiossa.
Taulukko:
Taulukon kiinteä pituus Matriisipituus on osa taulukkotyyppiä, joten [3]int ja [4]int ovat kaksi eri taulukkotyyppiä. Jos sitä ei ole määritetty, koko lasketaan automaattisesti Alustusparia ei voi muuttaa arvolla
viipale:
Viipaleen pituutta voidaan muuttaa. Kolmen attribuutin, osoitin, pituus ja kapasiteetti, kokoa ei tarvitse määrittää. Viipaletta välitetään osoitteen mukaan (pass by reference) ja ne voidaan alustaa taulukon tai sisäänrakennetun make(-funktion) kautta Alustuksen aikana len=cap laajenee.
Taulukot ovat arvotyyppejä ja viipaleet viitetyyppejä;
Taulukon pituus on kiinteä, mutta siivut eivät ole (viipaleet ovat dynaamisia taulukoita)
Viipaleen alla oleva kerros on taulukko
Go-kielen slice-laajennusmekanismi on erittäin taitava. Erityisesti, kun siivua on laajennettava, Go-kieli luo uuden taustalla olevan taulukon ja kopioi alkuperäisen taulukon tiedot uuteen taulukkoon. Sitten viipalosoitin osoittaa uuteen taulukkoon, pituus päivitetään alkuperäiseen pituuteen plus laajennettu pituus ja kapasiteetti päivitetään uuden taulukon pituuteen.
Leikkeen laajeneminen:
mennä 1.17
- // src/runtime/slice.go
-
- func growslice(et *_type, old slice, cap int) slice {
- // ...
-
- newcap := old.cap
- doublecap := newcap + newcap
- if cap > doublecap {
- newcap = cap
- } else {
- if old.cap < 1024 {
- newcap = doublecap
- } else {
- // Check 0 < newcap to detect overflow
- // and prevent an infinite loop.
- for 0 < newcap && newcap < cap {
- newcap += newcap / 4
- }
- // Set newcap to the requested cap when
- // the newcap calculation overflowed.
- if newcap <= 0 {
- newcap = cap
- }
- }
- }
-
- // ...
-
- return slice{p, old.len, newcap}
- }
Ennen kuin varaat muistitilan, sinun on määritettävä osion uusi kapasiteetti.
Jos odotettu kapasiteetti on suurempi kuin kaksinkertainen nykyinen kapasiteetti, käytetään odotettua kapasiteettia, jos nykyisen viipaleen pituus on alle 1024, kapasiteetti kaksinkertaistuu, jos nykyisen viipaleen pituus on suurempi tai yhtä suuri 1024, kapasiteettia lisätään 25 % joka kerta, kunnes uusi kapasiteetti Odotettua suurempi kapasiteetti;
mennä 1.18
- // src/runtime/slice.go
-
- func growslice(et *_type, old slice, cap int) slice {
- // ...
-
- newcap := old.cap
- doublecap := newcap + newcap
- if cap > doublecap {
- newcap = cap
- } else {
- const threshold = 256
- if old.cap < threshold {
- newcap = doublecap
- } else {
- // Check 0 < newcap to detect overflow
- // and prevent an infinite loop.
- for 0 < newcap && newcap < cap {
- // Transition from growing 2x for small slices
- // to growing 1.25x for large slices. This formula
- // gives a smooth-ish transition between the two.
- newcap += (newcap + 3*threshold) / 4
- }
- // Set newcap to the requested cap when
- // the newcap calculation overflowed.
- if newcap <= 0 {
- newcap = cap
- }
- }
- }
-
- // ...
-
- return slice{p, old.len, newcap}
- }
-
Ero edelliseen versioon on pääasiassa laajennuskynnyksessä ja tässä koodirivissä:newcap += (newcap + 3*threshold) / 4
。
Ennen kuin varaat muistitilan, sinun on määritettävä osion uusi kapasiteetti.
Jos odotettu kapasiteetti on suurempi kuin kaksi kertaa nykyinen kapasiteetti, odotettua kapasiteettia käytetään;
Jos nykyisen viipaleen pituus on pienempi kuin kynnys (oletus 256), kapasiteetti kaksinkertaistuu;
Jos nykyisen viipaleen pituus on suurempi tai yhtä suuri kuin kynnys (oletus 256), kapasiteettia lisätään 25 % joka kerta newcap + 3*threshold
, kunnes uusi kapasiteetti on suurempi kuin odotettu kapasiteetti;
Slice-laajennus on jaettu kahteen vaiheeseen, ennen go1.18:aa ja sen jälkeen:
1. Ennen lähtöä 1.18:
Jos odotettu kapasiteetti on suurempi kuin kaksi kertaa nykyinen kapasiteetti, odotettua kapasiteettia käytetään;
Jos nykyisen viipaleen pituus on pienempi kuin 1024, kapasiteetti kaksinkertaistuu;
Jos nykyisen viipaleen pituus on suurempi kuin 1024, kapasiteettia lisätään 25 % joka kerta, kunnes uusi kapasiteetti on suurempi kuin odotettu kapasiteetti;
2. Menon 1.18 jälkeen:
Jos odotettu kapasiteetti on suurempi kuin kaksi kertaa nykyinen kapasiteetti, odotettua kapasiteettia käytetään;
Jos nykyisen viipaleen pituus on pienempi kuin kynnys (oletus 256), kapasiteetti kaksinkertaistuu;
Jos nykyisen viipaleen pituus on suurempi tai yhtä suuri kuin kynnys (oletus 256), kapasiteettia lisätään 25 % joka kerta newcap + 3*threshold
, kunnes uusi kapasiteetti on suurempi kuin odotettu kapasiteetti;
go convey on yksikkötestauskehys, joka tukee golangia
go convey voi automaattisesti valvoa tiedostojen muutoksia ja aloittaa testejä sekä lähettää testitulokset verkkokäyttöliittymään reaaliajassa
go convey tarjoaa monipuolisia väitteitä testitapausten kirjoittamisen yksinkertaistamiseksi
Lykkäyksen tehtävä on:
Sinun tarvitsee vain lisätä avainsana lykätä ennen normaalin funktion tai menetelmän kutsumista lykkäämiseen vaaditun syntaksin suorittamiseksi. Kun lykkäyskäsky suoritetaan, lykkäystä seuraava toiminto lykätään. Defer-käskyn jälkeistä funktiota ei suoriteta ennen kuin defer-käskyn sisältävä funktio on suoritettu, riippumatta siitä, päättyykö defer-käskyn sisältävä funktio normaalisti returnin kautta vai päättyykö se epänormaalisti paniikin vuoksi. Voit suorittaa useita lykkäyskäskyjä funktiossa käänteisessä järjestyksessä niiden ilmoittamiselle.
Yleisiä lykkäysskenaarioita:
Defer-lausetta käytetään usein käsittelemään parillisia toimintoja, kuten avaamista, sulkemista, yhdistämistä, irrottamista, lukitsemista ja lukituksen vapauttamista.
Lykkäysmekanismin avulla, riippumatta siitä, kuinka monimutkainen toimintologiikka on, resurssien vapautuminen voidaan taata millä tahansa suorituspolulla.
Viipalointi on toteutettu taulukoiden perusteella. Koska se on toteutettu taulukoiden pohjalta, sen taustalla olevaa muistia varataan jatkuvasti, mikä on erittäin tehokasta Dataa voidaan saada myös indeksien kautta, ja se voidaan iteroida ja optimoida roskien keräämistä. Leikkeet itsessään eivät ole dynaamisia taulukoita tai taulukkoosoittimia. Sisäisesti toteutettu tietorakenne viittaa alla olevaan taulukkoon osoittimen kautta, ja asiaankuuluvat attribuutit on asetettu rajoittamaan tietojen luku- ja kirjoitustoiminnot tietylle alueelle. Itse viipale on vain luku -objekti, ja sen toimintamekanismi on samanlainen kuin taulukon osoittimen kapselointi.
Slice-objekti on hyvin pieni, koska se on tietorakenne, jossa on vain 3 kenttää:
osoitin alla olevaan taulukkoon
viipaleen pituus
viipaleen kapasiteetti
Go (1.17) -laajennuksen viipalointistrategia on seuraava:
Ensin arvioi, jos uusi kapasiteetti on suurempi kuin 2 kertaa vanha kapasiteetti, lopullinen kapasiteetti on uusi kapasiteetti.
Muussa tapauksessa, jos vanhan siivun pituus on alle 1024, lopullinen kapasiteetti on kaksi kertaa vanha kapasiteetti.
Muussa tapauksessa, jos vanha viipaleen pituus on suurempi tai yhtä suuri kuin 1024, lopullista kapasiteettia lisätään syklisesti 1/4 vanhasta kapasiteetista, kunnes lopullinen kapasiteetti on suurempi tai yhtä suuri kuin uusi pyydetty kapasiteetti.
Jos lopullinen kapasiteettilaskennan arvo ylittyy, lopullinen kapasiteetti on uusi haettu kapasiteetti.
Tilanne yksi:
Alkuperäisessä taulukossa on edelleen kapasiteettia, jota voidaan laajentaa (todellista kapasiteettia ei ole täytetty. Tässä tapauksessa laajennettu taulukko osoittaa edelleen alkuperäiseen taulukkoon). Viipale.
Tilanne kaksi:
Osoittautuu, että taulukon kapasiteetti on saavuttanut maksimiarvon. Jos haluat laajentaa kapasiteettia, Go avaa ensin muistialueen, kopioi alkuperäisen arvon ja suorittaa sitten append()-toiminnon. Tämä tilanne ei vaikuta alkuperäiseen taulukkoon ollenkaan. Jos haluat kopioida osan, on parasta käyttää Copy-toimintoa.
slice底层结构并没有使用加锁等方式,不支持并发读写,所以并不是线程安全的, 使用多个 goroutine 对类型为 slice 的变量进行操作,每次输出的值大概率都不会一样,与预期值不一致; slice在并发执行中不会报错,但是数据会丢失 如果想实现slice线程安全,有两种方式: 方式一:通过加锁实现slice线程安全,适合对性能要求不高的场景。
- func TestSliceConcurrencySafeByMutex(t *testing.T) {
- var lock sync.Mutex //互斥锁
- a := make([]int, 0)
- var wg sync.WaitGroup
- for i := 0; i < 10000; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
- lock.Lock()
- defer lock.Unlock()
- a = append(a, i)
- }(i)
- }
- wg.Wait()
- t.Log(len(a))
- // equal 10000
- }
-
方式二:通过channel实现slice线程安全,适合对性能要求高的场景。
- func TestSliceConcurrencySafeByChanel(t *testing.T) {
- buffer := make(chan int)
- a := make([]int, 0)
- // 消费者
- go func() {
- for v := range buffer {
- a = append(a, v)
- }
- }()
- // 生产者
- var wg sync.WaitGroup
- for i := 0; i < 10000; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
- buffer <- i
- }(i)
- }
- wg.Wait()
- t.Log(len(a))
- // equal 10000
- }