9.4.6.1 توضیحات #
الگوی Cancellation یا Context Cancellation Pattern یکی از تکنیکهای کلیدی در Go برای کنترل lifecycle goroutineها و جلوگیری از اجرای ناخواسته یا بیپایان آنهاست. هدف اصلی این الگو، ارسال سیگنال توقف به goroutineهایی است که به هر دلیلی باید عملیات خود را زودتر از موعد قطع کنند؛ مثلاً کاربر درخواست کنسل میدهد، تایماوت رخ میدهد یا رویداد خاصی در سیستم اتفاق میافتد.
در معماری idiomatic Go، برای پیادهسازی لغو عملیات، بهجای بستن کانالهای اختصاصی، از context.Context استفاده میشود که یک سازوکار استاندارد، ساده و thread-safe برای انتشار سیگنال لغو (cancelation) و همچنین مدیریت تایماوتها و مقادیر مرتبط است. معمولاً یک context اصلی با دستور context.WithCancel
یا context.WithTimeout
ساخته میشود و این context به تمامی goroutineها و تابعهای فرزند پاس داده میشود. هر goroutine به طور دورهای وضعیت context را بررسی میکند (با <-ctx.Done()
یا ctx.Err()
) و اگر سیگنال لغو صادر شده باشد، عملیات خود را متوقف میکند و منابع را آزاد میسازد.
استفاده از context علاوه بر خوانایی و سادگی، از مشکلات رایج مانند goroutine leak، deadlock یا بستن اشتباهی کانالها جلوگیری میکند و مدیریت همزمانی را ایمنتر میسازد. در پروژههای تولیدی Go، این الگو به عنوان استاندارد طلایی لغو عملیات (چه برای لغو دستی، چه Timeout و چه Propagation سیگنال لغو در عمق کالاستک) توصیه میشود.
9.4.6.2 دیاگرام #
<-ctx.Done() Main->>Ctx: فراخوانی cancel (یا رخداد timeout) Ctx-->>Worker1: ارسال سیگنال لغو Ctx-->>Worker2: ارسال سیگنال لغو Worker1->>Main: آزاد کردن منابع و پایان Worker2->>Main: آزاد کردن منابع و پایان
9.4.6.3 نمونه کد #
1package main
2
3import (
4 "context"
5 "fmt"
6 "time"
7)
8
9func main() {
10 // ساخت context قابل لغو
11 ctx, cancel := context.WithCancel(context.Background())
12
13 // اجرای goroutine با بررسی لغو
14 go func(ctx context.Context) {
15 for {
16 select {
17 case <-ctx.Done():
18 fmt.Println("Cancelled:", ctx.Err())
19 return
20 default:
21 fmt.Println("Running")
22 time.Sleep(time.Second)
23 }
24 }
25 }(ctx)
26
27 // اجرای goroutine برای ۳ ثانیه
28 time.Sleep(3 * time.Second)
29
30 // فراخوانی لغو
31 cancel()
32
33 // انتظار برای خاتمه goroutine
34 time.Sleep(time.Second)
35}
در کد فوق ما یک کانال از نوع ساختار ایجاد کردیم با عنوان cancel و این کانال را داخل یکی از case های select بصورت دریافت قرار دادیم که در ادامه ما یک Sleep ۳ گذاشتیم تا فرآیند انجام شود و Running چاپ شود. پس از آن کانال را close کردیم و سیگنال لغو فرآیند ارسال شد و گوروتین کاملا متوقف شد.
9.4.6.4 کاربردها #
- لغو یک کار طولانیمدت: عملکردی که عملیات زمانبر مانند درخواست شبکه یا محاسبات را انجام میدهد، اگر دیگر به آن نیاز نباشد یا از مهلت زمانی فراتر رود، میتوان آن را لغو کرد.
- پاکسازی منابع: تابعی که منابعی مانند فایل یا اتصال شبکه را تخصیص می دهد، می تواند لغو شود تا این منابع قبل از اینکه دیگر مورد نیاز نباشند آزاد شوند.
- خاتمه دادن به یک سرور: سروری که چندین درخواست را مدیریت میکند، میتواند با لغو تمام عملکردهای در حال اجرا که این درخواستها را انجام میدهند، بهخوبی خاموش شود.
- لغو یک کار پس زمینه: یک کار پس زمینه که همزمان با برنامه اصلی اجرا می شود را می توان لغو کرد تا از اجرای آن جلوگیری شود.