9.4.6 الگو Context Cancellation Pattern

9.4.6 الگو Context Cancellation Pattern

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 دیاگرام #

sequenceDiagram participant Main as Main Goroutine participant Ctx as Context (WithCancel/WithTimeout) participant Worker1 as Worker Goroutine 1 participant Worker2 as Worker Goroutine 2 Main->>Ctx: ساخت context با WithCancel یا WithTimeout Main->>Worker1: ارسال context Main->>Worker2: ارسال context Note over Worker1, Worker2: انجام عملیات و بررسی
<-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}
1$ go run main.go
2Running
3Running
4Running
5Running
6Cancelled: context canceled

در کد فوق ما یک کانال از نوع ساختار ایجاد کردیم با عنوان cancel و این کانال را داخل یکی از case های select بصورت دریافت قرار دادیم که در ادامه ما یک Sleep ۳ گذاشتیم تا فرآیند انجام شود و Running چاپ شود. پس از آن کانال را close کردیم و سیگنال لغو فرآیند ارسال شد و گوروتین کاملا متوقف شد.

9.4.6.4 کاربردها #

  • لغو یک کار طولانی‌مدت: عملکردی که عملیات زمان‌بر مانند درخواست شبکه یا محاسبات را انجام می‌دهد، اگر دیگر به آن نیاز نباشد یا از مهلت زمانی فراتر رود، می‌توان آن را لغو کرد.
  • پاکسازی منابع: تابعی که منابعی مانند فایل یا اتصال شبکه را تخصیص می دهد، می تواند لغو شود تا این منابع قبل از اینکه دیگر مورد نیاز نباشند آزاد شوند.
  • خاتمه دادن به یک سرور: سروری که چندین درخواست را مدیریت می‌کند، می‌تواند با لغو تمام عملکردهای در حال اجرا که این درخواست‌ها را انجام می‌دهند، به‌خوبی خاموش شود.
  • لغو یک کار پس زمینه: یک کار پس زمینه که همزمان با برنامه اصلی اجرا می شود را می توان لغو کرد تا از اجرای آن جلوگیری شود.