Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions content/chapter 3/3.4-atomic.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,163 @@ func main() {
2. **پیاده سازی ساختارهای داده با همزمانی سطح (high-concurrency) بالا :** با پکیج atomic می توان برای پیاده سازی ساختارهای داده ای استفاده کرد که برای دسترسی همزمان و اصلاح توسط چندین گوروتین ایمن هستند. به عنوان مثال، می توانید از بسته اتمی برای پیاده سازی نقشه یا صف همزمان استفاده کنید.

3. **پیاده سازی شمارنده (counter) از نوع atomic :** شما با استفاده از پکیج atomic می توانید برای افزایش و کاهش شمارنده ها به صورت اتمی که می تواند برای اجرای مواردی مانند شمارش مرجع یا محدود کردن ratelimit استفاده شود.

## 3.4.3 Compare & Swap (CAS) در atomic

به مثال زیر توجه کنید :

{{< play >}}
package main

import (
"fmt"
"sync"
"sync/atomic"
"time"
)

type Server struct {
active int32 // 0 = inactive, 1 = active
wg sync.WaitGroup
}

func (s *Server) Start() {
if atomic.CompareAndSwapInt32(&s.active, 0, 1) {
fmt.Println("Server starting...")
s.wg.Add(1)
go s.run()
} else {
fmt.Println("Server already running")
}
}

func (s *Server) Stop() {
if atomic.CompareAndSwapInt32(&s.active, 1, 0) {
fmt.Println("Server stopping...")
s.wg.Done()
} else {
fmt.Println("Server already stopped")
}
}

func (s *Server) run() {
defer fmt.Println("Server stopped")

for atomic.LoadInt32(&s.active) == 1 {
fmt.Println("Server processing...")
time.Sleep(500 * time.Millisecond)
}
}

func (s *Server) IsActive() bool {
return atomic.LoadInt32(&s.active) == 1
}

func main() {
server := &Server{}

// Start the server
server.Start()

// Check status concurrently
go func() {
time.Sleep(100 * time.Millisecond)
fmt.Printf("Server active: %v\n", server.IsActive())
}()

// Let it run for a bit
time.Sleep(2 * time.Second)

// Stop the server
server.Stop()

// Wait for graceful shutdown
server.wg.Wait()
}
{{< /play >}}

مثال فوق یک نمونه از پیاده‌سازی سرور با قابلیت مدیریت وضعیت و graceful shutdown در زبان Go است. ساختار Server از یک پرچم اتمی active برای نشان دادن وضعیت سرور (فعال/غیرفعال) و یک WaitGroup برای هماهنگی بین گوروتین‌ها استفاده می‌کند. متد Start با استفاده از atomic.CompareAndSwapInt32 به صورت اتمی وضعیت سرور را بررسی و تغییر می‌دهد و از راه‌اندازی مجدد سرور در حال اجرا جلوگیری می‌کند. متد run در یک گوروتین مجزا اجرا شده و با حلقه‌ای که وضعیت active را به صورت اتمی بررسی می‌کند، کار می‌کند تا زمانی که سرور فعال باشد. متد Stop نیز به صورت اتمی وضعیت سرور را تغییر داده و با فراخوانی Done روی WaitGroup، خاتمه سرور را اطلاع می‌دهد. در تابع main، سرور راه‌اندازی شده، وضعیت آن به صورت همزمان بررسی می‌شود و پس از مدت مشخصی، سرور متوقف می‌شود و با Wait برای اتمام graceful shutdown منتظر می‌ماند.

## 3.4.4 atomic Pointer

به مثال زیر توجه کنید :

{{< play >}}
package main

import (
"fmt"
"sync"
"sync/atomic"
"time"
"unsafe"
)

type Config struct {
timeout time.Duration
retries int
enabled bool
}

type AtomicConfigManager struct {
config unsafe.Pointer // *Config
}

func NewAtomicConfigManager(initial *Config) *AtomicConfigManager {
mgr := &AtomicConfigManager{}
atomic.StorePointer(&mgr.config, unsafe.Pointer(initial))
return mgr
}

func (m *AtomicConfigManager) UpdateConfig(newConfig *Config) {
atomic.StorePointer(&m.config, unsafe.Pointer(newConfig))
}

func (m *AtomicConfigManager) GetConfig() *Config {
return (*Config)(atomic.LoadPointer(&m.config))
}

func main() {
initialConfig := &Config{
timeout: 5 * time.Second,
retries: 3,
enabled: true,
}

mgr := NewAtomicConfigManager(initialConfig)

var wg sync.WaitGroup

// Reader goroutines
for i := range 5 {
wg.Add(1)
go func(id int) {
defer wg.Done()
for range 3 {
config := mgr.GetConfig()
fmt.Printf("Reader %d: timeout=%v, retries=%d, enabled=%v\n",
id, config.timeout, config.retries, config.enabled)
time.Sleep(100 * time.Millisecond)
}
}(i)
}

// Writer goroutine
wg.Add(1)
go func() {
defer wg.Done()

time.Sleep(150 * time.Millisecond)
mgr.UpdateConfig(&Config{
timeout: 10 * time.Second,
retries: 5,
enabled: false,
})
fmt.Println("Config updated!")
}()

wg.Wait()
}
{{< /play >}}

این کد یک مدیریت کانفیگ thread-safe را با استفاده از عملیات اتمی روی پوینتر در زبان Go پیاده‌سازی می‌کند. ساختار AtomicConfigManager از یک پوینتر اتمی برای ذخیره‌سازی اشاره‌گر به آبجکت Config استفاده می‌کند که با توابع atomic.StorePointer و atomic.LoadPointer مدیریت می‌شود. در تابع main، ابتدا یک کانفیگ اولیه ایجاد شده و سپس پنج گوروتین خواننده به طور مکرر کانفیگ فعلی را خوانده و مقادیر آن را چاپ می‌کنند، در حالی که یک گوروتین نویسنده پس از تأخیر کوتاه، کانفیگ جدیدی را جایگزین می‌کند. کلید کارایی این الگو در این است که خواننده‌ها همیشه یک نسخه consistent از کانفیگ را دریافت می‌کنند (حتی در حین به‌روزرسانی) بدون نیاز به قفل، زیرا عملیات خواندن و نوشتن پوینتر به صورت اتمی انجام شده و هر خواننده به نسخه قدیمی به نسخه جدید دسترسی پیدا می‌کند اما هیچگاه یک نسخه نیمه‌کاره مشاهده نمی‌کند. این الگو برای سناریوهای read-heavy که در آن reads مکرر و writes کم داریم بسیار کارآمد است.