моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Оглавление
1. Разница между маркой и новой
4. Что такое Go Convey? Для чего он обычно используется?
5.Каковы функции и характеристики отсрочки?
6. Базовая реализация среза Go? Механизм расширения Go Slice?
7. Одинаковы ли срезы до и после расширения?
8.Почему срез не является потокобезопасным?
Отличия: 1. Make можно использовать только для выделения и инициализации данных типов срез, карта и chan, тогда как new можно использовать любой тип данных;
2. Новое выделение возвращает указатель типа «*Type», а make возвращает ссылку типа Type;
3. Пространство, выделенное new, будет очищено после того, как make выделит пространство, и оно будет инициализировано.
Передача по значению приведет только к копированию значения параметра и помещению его в соответствующую функцию. Адреса двух переменных различны и не могут изменять друг друга.
Передача адреса (передача по ссылке) передаст саму переменную в соответствующую функцию, и содержимое значения переменной может быть изменено в функции.
Множество:
Фиксированная длина массива Длина массива является частью типа массива, поэтому [3]int и [4]int — это два разных типа массива. Для массива необходимо указать размер. Если он не указан, размер будет автоматически рассчитан на основе значения. пара инициализации. Массив не может быть изменен, передается по значению.
кусочек:
Длину среза можно изменить. Срез — это упрощенная структура данных. Для трех атрибутов: указатель, длина и емкость не требуется указывать размер. Срезы передаются по адресу (передаются по ссылке) и могут быть инициализированы через массив или с помощью встроенной функции make(). Во время инициализации затем расширяется len=cap.
Массивы — это типы значений, а срезы — это ссылочные типы;
Длина массивов фиксирована, а срезов — нет (срезы представляют собой динамические массивы).
Базовый слой среза представляет собой массив
Механизм расширения срезов в языке Go очень умен. Он обеспечивает расширение путем перераспределения базового массива и переноса данных в новый массив. В частности, когда необходимо расширить срез, язык Go создает новый базовый массив и копирует данные из исходного массива в новый массив. Затем указатель среза указывает на новый массив, длина обновляется до исходной длины плюс расширенная длина, а емкость обновляется до длины нового массива.
Чтобы добиться расширения среза:
перейти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}
- }
Прежде чем выделять пространство памяти, необходимо определить емкость нового слайса. Во время выполнения вы можете выбрать различные стратегии расширения в зависимости от текущей емкости слайса:
Если ожидаемая емкость более чем в два раза превышает текущую емкость, будет использоваться ожидаемая емкость, если длина текущего среза меньше 1024, емкость будет удвоена, если длина текущего среза больше или равна; 1024, емкость будет увеличиваться на 25% каждый раз, пока новая емкость не превысит ожидаемую;
перейти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}
- }
-
Отличие от предыдущей версии в основном заключается в пороге расширения и вот этой строке кода:newcap += (newcap + 3*threshold) / 4
。
Прежде чем выделять пространство памяти, необходимо определить емкость нового слайса. Во время выполнения вы можете выбрать различные стратегии расширения в зависимости от текущей емкости слайса:
Если ожидаемая мощность более чем в два раза превышает текущую мощность, будет использоваться ожидаемая мощность;
Если длина текущего слайса меньше порога (по умолчанию 256), емкость будет удвоена;
Если длина текущего слайса больше или равна порогу (по умолчанию 256), емкость будет увеличиваться на 25 % каждый раз. newcap + 3*threshold
, пока новая емкость не превысит ожидаемую;
Расширение среза разделено на два этапа: до и после go1.18:
1. Прежде чем идти 1.18:
Если ожидаемая мощность более чем в два раза превышает текущую мощность, будет использоваться ожидаемая мощность;
Если длина текущего слайса меньше 1024, емкость будет удвоена;
Если длина текущего среза превышает 1024, емкость будет увеличиваться на 25 % каждый раз, пока новая емкость не превысит ожидаемую;
2. После go1.18:
Если ожидаемая мощность более чем в два раза превышает текущую мощность, будет использоваться ожидаемая мощность;
Если длина текущего слайса меньше порога (по умолчанию 256), емкость будет удвоена;
Если длина текущего слайса больше или равна порогу (по умолчанию 256), емкость будет увеличиваться на 25 % каждый раз. newcap + 3*threshold
, пока новая емкость не превысит ожидаемую;
Go Transfer — это среда модульного тестирования, поддерживающая golang.
Go Transfer может автоматически отслеживать изменения файлов и запускать тесты, а также выводить результаты тестов в веб-интерфейс в режиме реального времени.
Go Transfer предоставляет множество утверждений, упрощающих написание тестовых примеров.
Функция отсрочки:
Вам нужно только добавить ключевое слово defer перед вызовом обычной функции или метода, чтобы завершить синтаксис, необходимый для defer. Когда выполняется оператор defer, функция, следующая за defer, будет отложена. Функция после defer не будет выполняться до тех пор, пока не будет выполнена функция, содержащая оператор defer, независимо от того, завершается ли функция, содержащая оператор defer, нормально через возврат или завершается ненормально из-за паники. Вы можете выполнить несколько операторов defer в функции в порядке, обратном их объявлению.
Распространенные сценарии отсрочки:
Оператор defer часто используется для обработки парных операций, таких как открытие, закрытие, подключение, отключение, блокировка и снятие блокировок.
Благодаря механизму отсрочки, независимо от сложности логики функции, можно гарантировать освобождение ресурсов на любом пути выполнения.
Срез реализован на основе массивов. Его базовый уровень представляет собой массив. Он сам по себе очень мал и может рассматриваться как абстракция базового массива. Поскольку он реализован на основе массивов, его базовая память выделяется непрерывно, что очень эффективно. Данные также можно получать с помощью индексов, оптимизации итерации и сборки мусора. Сами срезы не являются динамическими массивами или указателями массивов. Внутренне реализованная структура данных ссылается на базовый массив через указатель, а соответствующие атрибуты устанавливаются для ограничения операций чтения и записи данных указанной областью. Срез сам по себе является объектом только для чтения, и его рабочий механизм аналогичен инкапсуляции указателя массива.
Объект среза очень мал, поскольку представляет собой структуру данных всего с тремя полями:
указатель на базовый массив
длина среза
емкость среза
Стратегия расширения по срезам в Go (1.17) следующая:
Во-первых, оцените: если вновь примененная мощность более чем в 2 раза превышает старую мощность, окончательной мощностью будет вновь примененная мощность.
В противном случае, если длина старого среза меньше 1024, конечная емкость будет вдвое больше старой.
В противном случае, если длина старого слайса больше или равна 1024, конечная емкость будет циклически увеличиваться на 1/4 от старой емкости до тех пор, пока конечная емкость не станет больше или равна новой запрошенной емкости.
Если итоговое расчетное значение мощности переполняется, конечной мощностью является вновь запрошенная мощность.
Ситуация первая:
Исходный массив все еще имеет емкость, которую можно расширить (фактическая емкость не заполнена). В этом случае расширенный массив все еще указывает на исходный массив, и операция над срезом может повлиять на несколько указателей, указывающих на один и тот же адрес. Срез.
Ситуация вторая:
Оказывается, емкость массива достигла максимального значения. Если вы хотите расширить емкость, Go сначала откроет область памяти по умолчанию, скопирует исходное значение, а затем выполнит операцию добавления(). Эта ситуация вообще не влияет на исходный массив. Чтобы скопировать фрагмент, лучше всего использовать функцию копирования.
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
- }