Это представляет собой сложную задачу из-за множества компонентов, взаимодействующих друг с другом в различных сетевых условиях.
Проверка корректности отдельных компонентов системы. Используйте библиотеки для написания тестов, такие как
testing
в Go. Интеграционное тестирование Моки (mocking) и заглушки (stubs) для изоляции компонентов.package main
import (
"testing"
)
func TestAdd(t *testing.T) {
result := Add(1, 2)
if result != 3 {
t.Errorf("Expected 3, got %d", result)
}
}
Проверка взаимодействия между компонентами системы.
Используйте контейнеризацию (например, Docker) для поднятия среды тестирования. Тестирование с использованием реальных баз данных, очередей сообщений и других внешних сервисов.
version: '3'
services:
app:
image: myapp:test
depends_on:
- db
db:
image: postgres:latest
environment:
POSTGRES_DB: testdb
POSTGRES_USER: user
POSTGRES_PASSWORD: password
Проверка всей системы в целом, включая все её компоненты и их взаимодействие. Сценарии end-to-end (E2E) тестирования. Тестирование пользовательского интерфейса и API. Selenium для тестирования веб-интерфейсов. Postman или REST-assured для тестирования API.
Проверка системы на устойчивость к высоким нагрузкам и определение её производительности. Нагрузочные тесты для проверки, как система справляется с увеличением числа пользователей или запросов. Тесты стресс-тестирования для выявления пределов производительности системы. Тесты стабильности для проверки системы под постоянной нагрузкой в течение длительного времени. Apache JMeter Gatling Locust
Проверка способности системы справляться с отказами отдельных компонентов или сетевых сбоев. Имитация отказов компонентов (например, остановка сервиса или отключение узла).
Инструменты для хаос-тестирования (chaos engineering). Chaos Monkey от Netflix Gremlin
Проверка системы на наличие уязвимостей и обеспечение её защиты от атак.Сканы уязвимостей. Тестирование на проникновение (penetration testing). OWASP ZAP Burp Suite
Интеграция тестирования в процесс CI/CD для автоматизации и ускорения релизов. Настройка CI/CD конвейера для автоматического выполнения тестов при каждом изменении кода. Использование инструментов, таких как Jenkins, GitLab CI/CD или CircleCI.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Lock-free (без блокировок) концепция используется в параллельном и многопоточном программировании для создания алгоритмов, которые позволяют потокам работать с общими данными без традиционных блокировок, таких как мьютексы. Это помогает избежать проблем, связанных с блокировками, таких как взаимные блокировки и задержки.
Не используются мьютексы или другие блокирующие примитивы для управления доступом к ресурсам.
Обеспечивается параллельный доступ к данным, что повышает производительность многопоточных приложений.
Используются атомарные операции для предотвращения состояний гонки.
Атомарные операции гарантируют, что операция выполняется полностью или не выполняется вовсе, без промежуточных состояний.
Сравнивает текущее значение переменной с ожидаемым и, если они совпадают, обновляет её новым значением.
Атомарно увеличивает значение переменной и возвращает старое значение.
Используются атомарные операции для безопасного добавления и удаления элементов.
package main
import (
"fmt"
"sync/atomic"
"unsafe"
)
type Node struct {
value int
next *Node
}
type Stack struct {
head *Node
}
func (s *Stack) Push(value int) {
newNode := &Node{value: value}
for {
oldHead := s.head
newNode.next = oldHead
if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&s.head)), unsafe.Pointer(oldHead), unsafe.Pointer(newNode)) {
break
}
}
}
func (s *Stack) Pop() (int, bool) {
for {
oldHead := s.head
if oldHead == nil {
return 0, false
}
newHead := oldHead.next
if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&s.head)), unsafe.Pointer(oldHead), unsafe.Pointer(newHead)) {
return oldHead.value, true
}
}
}
func main() {
stack := &Stack{}
stack.Push(1)
stack.Push(2)
value, ok := stack.Pop()
if ok {
fmt.Println("Popped:", value)
} else {
fmt.Println("Stack is empty")
}
}
Потоки не блокируют друг друга, что устраняет проблему deadlock.
Параллельный доступ к данным без блокировок повышает производительность.
Снижаются затраты на контекстные переключения и синхронизацию.
Lock-free алгоритмы сложны в реализации и отладке.
Не все языки и платформы полностью поддерживают атомарные операции.
Частые повторные попытки атомарных операций могут ухудшить масштабируемость.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это мера зависимости одного модуля или компонента системы от других модулей. Высокое сцепление означает, что модули сильно зависят друг от друга, в то время как низкое сцепление указывает на слабую взаимосвязь между модулями.
Модули сильно зависят друг от друга.
Изменения в одном модуле могут потребовать изменений в других.
Сложнее тестировать и поддерживать код.
type User struct {
Name string
Age int
}
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
type UserRepository struct{}
func (r *UserRepository) FindById(id int) *User {
// Логика доступа к базе данных
return &User{Name: "John", Age: 30}
}
Модули имеют минимальные зависимости друг от друга.
Легче изменять, тестировать и повторно использовать код.
Использование интерфейсов и зависимостей через инъекцию (Dependency Injection).
type User struct {
Name string
Age int
}
type UserRepository interface {
FindById(id int) *User
}
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
Это мера того, насколько элементы внутри модуля или компонента связаны и работают вместе для выполнения одной задачи. Высокая связанность означает, что элементы внутри модуля тесно связаны и фокусируются на одной задаче, в то время как низкая связанность указывает на наличие разнородных элементов, выполняющих несвязанные задачи.
Модуль выполняет несколько различных задач.
Трудно поддерживать и изменять код.
Класс, выполняющий и доступ к базе данных, и логику бизнес-правил, и обработку пользовательского интерфейса.
type UserService struct {
// Доступ к базе данных
db Database
// Логика бизнес-правил
businessLogic BusinessLogic
// Логика уведомлений
notificationService NotificationService
}
func (s *UserService) ProcessUser(id int) {
user := s.db.FindUserById(id)
s.businessLogic.ApplyRules(user)
s.notificationService.Notify(user)
}
Модуль сфокусирован на выполнении одной задачи.
Легче понимать, поддерживать и изменять код.
Класс, выполняющий только одну роль, такую как управление пользователями или обработка платежей.
type UserService struct {
userRepository UserRepository
}
func (s *UserService) GetUser(id int) *User {
return s.userRepository.FindById(id)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это инструмент для профилирования производительности программ на Go, который позволяет собирать и анализировать информацию о потреблении ресурсов (таких как память и процессорное время). Он предоставляет удобные инструменты для визуализации и анализа профилей.
Для включения профилирования в вашем приложении, вам нужно импортировать пакет
net/http/pprof
и запустить HTTP-сервер.package main
import (
_ "net/http/pprof"
"log"
"net/http"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Ваше основное приложение
select {}
}
После запуска приложения с включенным
pprof
, вы можете собирать профили памяти с помощью HTTP-запросов. go tool pprof http://localhost:6060/debug/pprof/heap
Для получения профиля CPU:
go tool pprof http://localhost:6060/debug/pprof/profile
После того как вы собрали профиль, вы можете анализировать его с помощью команды
go tool pprof
. Это откроет интерактивный интерфейс, где вы можете использовать различные команды для анализа профиля.go tool pprof http://localhost:6060/debug/pprof/heap
Показывает функции, которые потребляют больше всего памяти.
Показывает использование памяти в конкретной функции.
Генерирует и открывает графический отчет в веб-браузере.
package main
import (
_ "net/http/pprof"
"log"
"net/http"
"time"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Создаем массив для потребления памяти
data := make([]byte, 100*1024*1024) // 100MB
// Заполняем массив данными
for i := range data {
data[i] = byte(i % 256)
}
// Ожидаем завершения приложения
time.Sleep(time.Hour)
}
pprof предоставляет детализированную информацию о потреблении памяти и процессорного времени.
Возможность визуализации профилей в виде графов и диаграмм.
Легко интегрируется в Go-приложения.
Профилирование может добавить накладные расходы, что может повлиять на производительность приложения.
Интерпретация результатов профилирования может быть сложной задачей и требует определенных навыков и опыта.
Ставь 👍 и забирай 📚 Базу знани
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
pprof позволяет профилировать производительность приложения в реальном времени, собирая данные о загрузке процессора, памяти и других метриках. Включите pprof в ваше приложение и запустите его на продакшене:
package main
import (
_ "net/http/pprof"
"log"
"net/http"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
select {}
}
Затем соберите и проанализируйте профили:
go tool pprof http://localhost:6060/debug/pprof/profile
Помогает отслеживать производительность и выявлять аномалии. Логируйте время выполнения критических операций и ошибки. Используйте библиотеку
log
или сторонние решения, такие как logrus
.package main
import (
"log"
"time"
)
func main() {
start := time.Now()
// Выполнение задачи
log.Printf("Task completed in %s", time.Since(start))
}
Позволяет отслеживать путь запроса через различные сервисы и компоненты системы, выявляя медленные части. Используйте OpenTelemetry для интеграции трассировки.
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
func main() {
tracer := otel.Tracer("example-tracer")
ctx, span := tracer.Start(context.Background(), "example-span")
defer span.End()
// Ваш код здесь
}
Собирайте метрики производительности и настройте алерты для выявления аномалий. Используйте Prometheus для сбора метрик и Grafana для визуализации.
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
)
var (
requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "Duration of HTTP requests.",
})
)
func init() {
prometheus.MustRegister(requestDuration)
}
func handler(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(requestDuration)
defer timer.ObserveDuration()
// Ваш код здесь
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Помогает выявить ошибки и проблемы производительности. Используйте системы сбора логов, такие как ELK Stack (Elasticsearch, Logstash, Kibana).
Облачные сервисы мониторинга, такие как New Relic, Datadog или AWS CloudWatch, предоставляют мощные инструменты для мониторинга и анализа производительности.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Вы можете использовать пакет
net/http/pprof
, который позволяет собирать профили производительности. Это включает в себя профилирование CPU, памяти, блокировок и других метрик. Пакет
net/http/pprof
автоматически регистрирует несколько обработчиков HTTP, которые позволяют собирать профили.Настройте и запустите HTTP-сервер для обработки запросов профилирования.
package main
import (
_ "net/http/pprof" // Профилирование
"log"
"net/http"
)
func main() {
// Запускаем HTTP-сервер для профилирования
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Ваше основное приложение
runApplication()
}
func runApplication() {
// Основная логика вашего приложения
for {
// Симуляция работы приложения
}
}
После запуска вашего приложения с включенным профилированием, вы можете получить доступ к различным профилям через браузер или командную строку.
Heap (Использование памяти):
http://localhost:6060/debug/pprof/heap
Profile (Профиль CPU):
http://localhost:6060/debug/pprof/profile?seconds=30
Block (Профиль блокировок):
http://localhost:6060/debug/pprof/block
Goroutine (Профиль горутин):
http://localhost:6060/debug/pprof/goroutine
Full Index (Полный список доступных профилей):
http://localhost:6060/debug/pprof/
Сбор профиля CPU
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
После сбора профиля
go tool pprof
откроет интерактивный интерфейс для анализа.go tool pprof <profile_file>
Показать функции, потребляющие больше всего ресурсов.
Показать использование ресурсов в конкретной функции.
Сгенерировать и открыть графический отчет в веб-браузере.
Пример использования команды top
(pprof) top
Showing nodes accounting for 80, 100% of 80 total
Showing top 10 nodes out of 12
flat flat% sum% cum cum%
50 62.50% 62.50% 50 62.50% main.runApplication
20 25.00% 87.50% 20 25.00% runtime.mstart
10 12.50% 100.00% 10 12.50% runtime.main
0 0% 100.00% 10 12.50% runtime.goexit
0 0% 100.00% 10 12.50% runtime.main.func1
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Использование может добавить накладные расходы (overhead) на выполнение вашего приложения. Это связано с тем, что сбор и обработка данных для профилирования требуют дополнительных ресурсов процессора и памяти.
Профилирование CPU может добавлять нагрузку, так как профайлер должен периодически собирать информацию о состоянии выполняемого кода. Профилирование включает периодические прерывания и сбор информации о стеке вызовов, что может замедлить выполнение программы.
Профилирование памяти (heap профилирование) требует отслеживания выделения и освобождения памяти, что может увеличить потребление памяти. Дополнительные структуры данных для хранения информации о профилях также требуют памяти.
Влияние на производительность может варьироваться в зависимости от сложности приложения и частоты сбора данных. В высоконагруженных системах накладные расходы могут быть более заметными.
Включайте профилирование только тогда, когда это действительно необходимо. Например, можно включать профилирование на ограниченный период для сбора данных, а затем отключать его.
Для профилирования CPU можно настроить частоту сбора данных. По умолчанию, pprof собирает данные каждые 10 миллисекунд. Вы можете уменьшить эту частоту.
import (
"runtime"
"runtime/pprof"
"os"
"time"
)
func main() {
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// Ваш код здесь
time.Sleep(30 * time.Second) // Выполнение в течение 30 секунд
}
Логируйте только важную информацию и избегайте чрезмерного логирования, чтобы минимизировать накладные расходы.
Используйте конкретные профили (например, heap, goroutine, block) только для тех аспектов, которые вы хотите анализировать. Это уменьшит накладные расходы по сравнению с полным профилированием.
package main
import (
"log"
"net/http"
"os"
"runtime/pprof"
"time"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Включаем профилирование CPU
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// Ваше основное приложение
runApplication()
}
func runApplication() {
// Основная логика вашего приложения
time.Sleep(30 * time.Second) // Симуляция работы приложения
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это структуры данных, которые обеспечивают синхронизацию и взаимодействие между потоками (горутинами) без использования традиционных блокировок (мьютексов). Вместо блокировок они полагаются на атомарные операции, такие как Compare-And-Swap (CAS), для обеспечения корректного и последовательного доступа к данным.
Они не используют мьютексы или другие блокирующие механизмы.
Они уменьшают задержки и повышают производительность за счет минимизации времени ожидания потоков.
Так как нет блокировок, невозможно возникновение взаимных блокировок.
Атомарные счетчики:
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int32
atomic.AddInt32(&counter, 1)
fmt.Println(atomic.LoadInt32(&counter)) // Вывод: 1
}
Lock-Free очередь (псевдокод):
package main
import (
"fmt"
"sync/atomic"
"unsafe"
)
type Node struct {
value int
next unsafe.Pointer
}
type LockFreeQueue struct {
head unsafe.Pointer
tail unsafe.Pointer
}
func NewLockFreeQueue() *LockFreeQueue {
node := unsafe.Pointer(&Node{})
return &LockFreeQueue{head: node, tail: node}
}
func (q *LockFreeQueue) Enqueue(value int) {
node := &Node{value: value}
for {
tail := atomic.LoadPointer(&q.tail)
next := atomic.LoadPointer(&(*Node)(tail).next)
if next == nil {
if atomic.CompareAndSwapPointer(&(*Node)(tail).next, next, unsafe.Pointer(node)) {
atomic.CompareAndSwapPointer(&q.tail, tail, unsafe.Pointer(node))
return
}
} else {
atomic.CompareAndSwapPointer(&q.tail, tail, next)
}
}
}
func (q *LockFreeQueue) Dequeue() (int, bool) {
for {
head := atomic.LoadPointer(&q.head)
tail := atomic.LoadPointer(&q.tail)
next := atomic.LoadPointer(&(*Node)(head).next)
if head == tail {
if next == nil {
return 0, false // очередь пуста
}
atomic.CompareAndSwapPointer(&q.tail, tail, next)
} else {
value := (*Node)(next).value
if atomic.CompareAndSwapPointer(&q.head, head, next) {
return value, true
}
}
}
}
func main() {
q := NewLockFreeQueue()
q.Enqueue(1)
q.Enqueue(2)
value, ok := q.Dequeue()
if ok {
fmt.Println("Dequeued:", value) // Вывод: Dequeued: 1
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Механизм отложенных вызовов (
defer
) и восстановления (recover
) используется для обработки паник, что позволяет безопасно завершить выполнение программы или выполнить необходимые действия при возникновении ошибок. Вызовы, обернутые в
defer
, выполняются в обратном порядке по завершении функции, в которой они объявлены, даже если возникла паника.recover
перехватывает панику, если она вызывается внутри функции, отложенной с помощью defer
. Если recover
вызывается вне контекста паники, он возвращает nil
.package main
import (
"fmt"
)
func main() {
fmt.Println("Starting the program...")
safeFunction()
fmt.Println("Program finished successfully.")
}
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
riskyFunction()
}
func riskyFunction() {
fmt.Println("About to cause a panic...")
panic("Something went wrong!")
fmt.Println("This line will not be executed.")
}
Запускает программу и вызывает
safeFunction
.Печатает сообщение "Starting the program...".
Содержит отложенную анонимную функцию, которая вызывает
recover
для обработки возможной паники.Вызывает
riskyFunction
.Печатает сообщение "About to cause a panic...".
Вызывает панику с сообщением "Something went wrong!". Когда
riskyFunction
вызывает панику, управление передается отложенной функции в safeFunction
, которая вызывает recover
. recover
перехватывает панику, и программа продолжает выполнение.Starting the program...
About to cause a panic...
Recovered from panic: Something went wrong!
Program finished successfully.
Можно использовать
defer
и recover
для обеспечения выполнения необходимых действий перед завершением программы, даже если возникла паника. Например, закрытие файлов, освобождение ресурсов и т.д.package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
panic(err)
}
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
file.Close()
fmt.Println("File closed")
}()
// Работа с файлом
panic("Something went wrong during file processing")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Может быть как безопасным, так и небезопасным, в зависимости от контекста и конкретного использования.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := make([]byte, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
buffer[i] = byte(i) // Несколько горутин одновременно модифицируют буфер
}(i)
}
wg.Wait()
fmt.Println("Buffer:", buffer)
}
Если несколько горутин только читают из буфера, это безопасно. Пример безопасного использования:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")
for i := 0; i < len(buffer); i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("buffer[%d]: %c\n", i, buffer[i])
}(i)
}
wg.Wait()
}
Для синхронизации доступа к буферу используйте мьютексы (
sync.Mutex
). Это гарантирует, что только одна горутина в данный момент модифицирует буфер.package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
buffer := make([]byte, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mu.Lock()
buffer[i] = byte(i)
mu.Unlock()
}(i)
}
wg.Wait()
fmt.Println("Buffer:", buffer)
}
Если каждая горутина должна работать с независимой копией буфера, создавайте копии для каждой горутины.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
localBuffer := make([]byte, len(buffer))
copy(localBuffer, buffer)
localBuffer[i] = byte(i)
fmt.Printf("Goroutine %d: %s\n", i, localBuffer)
}(i)
}
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Используется для обеспечения эксклюзивного доступа к общему ресурсу. Когда мьютекс заблокирован одной горутиной, все другие горутины, пытающиеся заблокировать этот же мьютекс, будут заблокированы до тех пор, пока первый мьютекс не будет разблокирован.
Блокирует мьютекс. Если мьютекс уже заблокирован, текущая горутина блокируется до тех пор, пока мьютекс не будет освобожден.
Разблокирует мьютекс. Если есть другие горутины, ожидающие блокировки этого мьютекса, одна из них будет разблокирована и сможет продолжить выполнение.
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
sync.RWMutex
(мьютекс для чтения и записи) предоставляет два типа блокировок:Для чтения (
RLock
): Позволяет нескольким горутинам одновременно читать данные.Для записи (
Lock
): Обеспечивает эксклюзивный доступ для записи, блокируя все другие операции чтения и записи.RLock()
Блокирует мьютекс для чтения. Если мьютекс уже заблокирован для записи, текущая горутина блокируется до тех пор, пока мьютекс не будет освобожден.
RUnlock()
Разблокирует мьютекс для чтения.
Lock()
Блокирует мьютекс для записи. Если мьютекс заблокирован для чтения или записи, текущая горутина блокируется до тех пор, пока мьютекс не будет освобожден.
Unlock()
Разблокирует мьютекс для записи.
package main
import (
"fmt"
"sync"
)
var rwMu sync.RWMutex
var counter int
func readCounter() int {
rwMu.RLock()
defer rwMu.RUnlock()
return counter
}
func increment() {
rwMu.Lock()
counter++
rwMu.Unlock()
}
func main() {
var wg sync.WaitGroup
// Запуск нескольких горутин для чтения
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Read Counter:", readCounter())
}()
}
// Запуск нескольких горутин для записи
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
sync.Mutex
: Блокирует доступ ко всем операциям (чтение и запись).sync.RWMutex
: Позволяет одновременное чтение, но блокирует доступ при записи.sync.Mutex
: Подходит для сценариев с частыми операциями записи или равномерным распределением чтений и записей.sync.RWMutex
: Подходит для сценариев с частыми операциями чтения и редкими операциями записи.sync.Mutex
: Используйте, когда необходимо обеспечить эксклюзивный доступ к ресурсу без различий между операциями чтения и записи.sync.RWMutex
: Используйте, когда операции чтения преобладают над операциями записи, чтобы повысить производительность за счет одновременного чтения.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM