τα στοιχεία επικοινωνίας μου
Ταχυδρομείο[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Πίνακας περιεχομένων
1.Η διαφορά μεταξύ κατασκευής και νέου
4.Τι είναι το Go Convey; Σε τι χρησιμοποιείται γενικά;
5. Ποιες είναι οι λειτουργίες και τα χαρακτηριστικά του defer;
6.Η υποκείμενη εφαρμογή του Go slice; Go slice μηχανισμό επέκτασης;
7. Είναι οι φέτες ίδιες πριν και μετά την επέκταση;
8. Γιατί το slice δεν είναι ασφαλές για το νήμα;
Διαφορές: 1. Το Make μπορεί να χρησιμοποιηθεί μόνο για την κατανομή και την προετοιμασία δεδομένων τύπων slice, map και chan ενώ το new μπορεί να εκχωρήσει οποιονδήποτε τύπο δεδομένων.
2. Η νέα κατανομή επιστρέφει έναν δείκτη, ο οποίος είναι ο τύπος "*Τύπος", ενώ το make επιστρέφει μια αναφορά, η οποία είναι Τύπος.
3. Ο χώρος που έχει εκχωρηθεί από το νέο θα εκκαθαριστεί αφού το make εκχωρήσει το χώρο, θα αρχικοποιηθεί.
Η μετάβαση με τιμή θα αντιγράψει μόνο την τιμή της παραμέτρου και θα την τοποθετήσει στην αντίστοιχη συνάρτηση Οι διευθύνσεις των δύο μεταβλητών είναι διαφορετικές και δεν μπορούν να τροποποιήσουν η μία την άλλη.
Η μετάδοση διεύθυνσης (πέρασμα με αναφορά) θα μεταβιβάσει την ίδια τη μεταβλητή στην αντίστοιχη συνάρτηση και το περιεχόμενο της τιμής της μεταβλητής μπορεί να τροποποιηθεί στη συνάρτηση.
Πίνακας:
Σταθερό μήκος πίνακα Το μήκος του πίνακα είναι μέρος του τύπου πίνακα, επομένως το [3]int και το [4]int είναι δύο διαφορετικοί τύποι πίνακα, εάν δεν καθορίζεται, το μέγεθος θα υπολογιστεί αυτόματα ζεύγος προετοιμασίας Ο πίνακας δεν μπορεί να αλλάξει
φέτα:
Το μήκος μιας φέτας μπορεί να αλλάξει. Ένα slice είναι μια ελαφριά δομή δεδομένων. Τα slices περνούν μέσω διεύθυνσης (pass by reference) και μπορούν να αρχικοποιηθούν μέσω ενός πίνακα ή μέσω της ενσωματωμένης συνάρτησης make().
Οι πίνακες είναι τύποι τιμών και οι φέτες είναι τύποι αναφοράς.
Το μήκος των πινάκων είναι σταθερό, αλλά οι φέτες δεν είναι (οι φέτες είναι δυναμικοί πίνακες)
Το υποκείμενο στρώμα της φέτας είναι ένας πίνακας
Ο μηχανισμός επέκτασης slice της γλώσσας Go είναι πολύ έξυπνος. Επιτυγχάνει την επέκταση με την ανακατανομή του υποκείμενου πίνακα και τη μετεγκατάσταση δεδομένων σε έναν νέο πίνακα. Συγκεκριμένα, όταν ένα slice χρειάζεται να επεκταθεί, η γλώσσα Go δημιουργεί έναν νέο υποκείμενο πίνακα και αντιγράφει τα δεδομένα στον αρχικό πίνακα στον νέο πίνακα. Στη συνέχεια, ο δείκτης φέτας δείχνει στον νέο πίνακα, το μήκος ενημερώνεται στο αρχικό μήκος συν το διευρυμένο μήκος και η χωρητικότητα ενημερώνεται στο μήκος του νέου πίνακα.
Για να επιτύχετε επέκταση φέτας:
go1.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}
- }
Πριν εκχωρήσετε χώρο στη μνήμη, πρέπει να προσδιορίσετε τη νέα χωρητικότητα του slice Κατά το χρόνο εκτέλεσης, μπορείτε να επιλέξετε διαφορετικές στρατηγικές για επέκταση με βάση την τρέχουσα χωρητικότητα του slice.
Εάν η αναμενόμενη χωρητικότητα είναι μεγαλύτερη από το διπλάσιο της τρέχουσας χωρητικότητας, η αναμενόμενη χωρητικότητα θα χρησιμοποιηθεί εάν το μήκος της τρέχουσας φέτας είναι μικρότερο από 1024, η χωρητικότητα θα διπλασιαστεί εάν το μήκος της τρέχουσας φέτας είναι 1024, η χωρητικότητα θα αυξάνεται κατά 25% κάθε φορά μέχρι τη νέα χωρητικότητα Μεγαλύτερη από την αναμενόμενη χωρητικότητα.
go1.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}
- }
-
Η διαφορά από την προηγούμενη έκδοση είναι κυρίως στο όριο επέκτασης και σε αυτήν τη γραμμή κώδικα:newcap += (newcap + 3*threshold) / 4
。
Πριν εκχωρήσετε χώρο στη μνήμη, πρέπει να προσδιορίσετε τη νέα χωρητικότητα του slice Κατά το χρόνο εκτέλεσης, μπορείτε να επιλέξετε διαφορετικές στρατηγικές για επέκταση με βάση την τρέχουσα χωρητικότητα του slice.
Εάν η αναμενόμενη χωρητικότητα είναι μεγαλύτερη από το διπλάσιο της τρέχουσας χωρητικότητας, θα χρησιμοποιηθεί η αναμενόμενη χωρητικότητα.
Εάν το μήκος του τρέχοντος κομματιού είναι μικρότερο από το όριο (προεπιλογή 256), η χωρητικότητα θα διπλασιαστεί.
Εάν το μήκος του τρέχοντος κομματιού είναι μεγαλύτερο ή ίσο με το όριο (προεπιλογή 256), η χωρητικότητα θα αυξάνεται κατά 25% κάθε φορά newcap + 3*threshold
, έως ότου η νέα χωρητικότητα είναι μεγαλύτερη από την αναμενόμενη χωρητικότητα.
Η επέκταση φέτας χωρίζεται σε δύο στάδια, πριν και μετά το go1.18:
1. Πριν πάτε 1.18:
Εάν η αναμενόμενη χωρητικότητα είναι μεγαλύτερη από το διπλάσιο της τρέχουσας χωρητικότητας, θα χρησιμοποιηθεί η αναμενόμενη χωρητικότητα.
Εάν το μήκος του τρέχοντος κομματιού είναι μικρότερο από 1024, η χωρητικότητα θα διπλασιαστεί.
Εάν το μήκος του τρέχοντος κομματιού είναι μεγαλύτερο από 1024, η χωρητικότητα θα αυξάνεται κατά 25% κάθε φορά έως ότου η νέα χωρητικότητα είναι μεγαλύτερη από την αναμενόμενη χωρητικότητα.
2. Μετά το go1.18:
Εάν η αναμενόμενη χωρητικότητα είναι μεγαλύτερη από το διπλάσιο της τρέχουσας χωρητικότητας, θα χρησιμοποιηθεί η αναμενόμενη χωρητικότητα.
Εάν το μήκος του τρέχοντος κομματιού είναι μικρότερο από το όριο (προεπιλογή 256), η χωρητικότητα θα διπλασιαστεί.
Εάν το μήκος του τρέχοντος κομματιού είναι μεγαλύτερο ή ίσο με το όριο (προεπιλογή 256), η χωρητικότητα θα αυξάνεται κατά 25% κάθε φορά newcap + 3*threshold
, έως ότου η νέα χωρητικότητα είναι μεγαλύτερη από την αναμενόμενη χωρητικότητα.
Το go convey είναι ένα πλαίσιο δοκιμών μονάδας που υποστηρίζει το golang
Το go convey μπορεί να παρακολουθεί αυτόματα τις τροποποιήσεις αρχείων και να ξεκινά δοκιμές και μπορεί να εξάγει αποτελέσματα δοκιμών στη διεπαφή ιστού σε πραγματικό χρόνο
Το go convey παρέχει πλούσιους ισχυρισμούς για να απλοποιήσει τη σύνταξη των δοκιμαστικών περιπτώσεων
Η λειτουργία της αναβολής είναι:
Χρειάζεται μόνο να προσθέσετε τη λέξη-κλειδί defer πριν καλέσετε μια κανονική συνάρτηση ή μέθοδο για να ολοκληρώσετε τη σύνταξη που απαιτείται για την αναβολή. Όταν εκτελείται η εντολή defer, η συνάρτηση που ακολουθεί την αναβολή θα αναβληθεί. Η συνάρτηση μετά την αναβολή δεν θα εκτελεστεί έως ότου εκτελεστεί η συνάρτηση που περιέχει τη δήλωση αναβολής, ανεξάρτητα από το εάν η συνάρτηση που περιέχει τη δήλωση αναβολής τελειώνει κανονικά μέσω επιστροφής ή τελειώνει ασυνήθιστα λόγω πανικού. Μπορείτε να εκτελέσετε πολλές εντολές αναβολής σε μια συνάρτηση, με την αντίστροφη σειρά της δήλωσης τους.
Συνήθη σενάρια αναβολής:
Η δήλωση αναβολής χρησιμοποιείται συχνά για τον χειρισμό ζευγαρωμένων λειτουργιών, όπως το άνοιγμα, το κλείσιμο, η σύνδεση, η αποσύνδεση, το κλείδωμα και η απελευθέρωση κλειδαριών.
Μέσω του μηχανισμού αναβολής, ανεξάρτητα από το πόσο πολύπλοκη είναι η λογική της συνάρτησης, μπορούμε να εγγυηθούμε ότι οι πόροι θα απελευθερωθούν σε οποιαδήποτε διαδρομή εκτέλεσης.
Ο τεμαχισμός υλοποιείται με βάση τους πίνακες. Το υποκείμενο στρώμα του είναι ένας πίνακας. Επειδή υλοποιείται με βάση πίνακες, η υποκείμενη μνήμη του κατανέμεται συνεχώς, κάτι που είναι πολύ αποτελεσματικό. Οι ίδιες οι φέτες δεν είναι δυναμικοί πίνακες ή δείκτες πίνακα. Η δομή δεδομένων που υλοποιείται εσωτερικά αναφέρεται στον υποκείμενο πίνακα μέσω ενός δείκτη και τα σχετικά χαρακτηριστικά ορίζονται για να περιορίζουν τις λειτουργίες ανάγνωσης και εγγραφής δεδομένων σε μια καθορισμένη περιοχή. Το ίδιο το slice είναι ένα αντικείμενο μόνο για ανάγνωση και ο μηχανισμός λειτουργίας του είναι παρόμοιος με την ενθυλάκωση ενός δείκτη πίνακα.
Το αντικείμενο slice είναι πολύ μικρό επειδή είναι μια δομή δεδομένων με μόνο 3 πεδία:
δείκτη στον υποκείμενο πίνακα
μήκος φέτας
χωρητικότητα φέτας
Η στρατηγική για τον τεμαχισμό της επέκτασης στο Go (1.17) είναι η εξής:
Πρώτον, κρίνετε, εάν η προσφάτως εφαρμοζόμενη χωρητικότητα είναι μεγαλύτερη από 2 φορές την παλιά χωρητικότητα, η τελική χωρητικότητα θα είναι η χωρητικότητα που εφαρμόστηκε πρόσφατα.
Διαφορετικά, εάν το μήκος της παλιάς φέτας είναι μικρότερο από 1024, η τελική χωρητικότητα θα είναι διπλάσια από την παλιά χωρητικότητα.
Διαφορετικά, εάν το μήκος του παλιού τεμαχίου είναι μεγαλύτερο ή ίσο με 1024, η τελική χωρητικότητα θα αυξηθεί κυκλικά κατά 1/4 από την παλιά χωρητικότητα έως ότου η τελική χωρητικότητα είναι μεγαλύτερη ή ίση με τη χωρητικότητα που ζητήθηκε πρόσφατα.
Εάν η τιμή υπολογισμού της τελικής χωρητικότητας υπερχειλίσει, η τελική χωρητικότητα είναι η προσφάτως ζητούμενη χωρητικότητα.
Κατάσταση πρώτη:
Ο αρχικός πίνακας εξακολουθεί να έχει χωρητικότητα που μπορεί να επεκταθεί (η πραγματική χωρητικότητα δεν έχει συμπληρωθεί, ο ανεπτυγμένος πίνακας εξακολουθεί να δείχνει προς τον αρχικό πίνακα Φέτα.
Κατάσταση δύο:
Αποδεικνύεται ότι η χωρητικότητα του πίνακα έχει φτάσει τη μέγιστη τιμή Εάν θέλετε να επεκτείνετε τη χωρητικότητα, το Go θα ανοίξει πρώτα μια περιοχή μνήμης από προεπιλογή, θα αντιγράψει την αρχική τιμή και στη συνέχεια θα εκτελέσει τη λειτουργία append(). Αυτή η κατάσταση δεν επηρεάζει καθόλου τον αρχικό πίνακα. Για να αντιγράψετε ένα Slice, είναι καλύτερο να χρησιμοποιήσετε τη λειτουργία Αντιγραφή.
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
- }