Skip to content

Commit 5572ff0

Browse files
committed
Add Channel Cancellation pattern explanation in Go
Introduces documentation for the Channel Cancellation pattern in Go, detailing its purpose, implementation, and practical use cases. Includes a conceptual overview, a sequence diagram, and example code to demonstrate how to coordinate goroutine termination using a shared cancellation channel. Highlights its advantages, such as simplicity, compatibility with Go's concurrency model, and suitability for event-driven designs. Describes scenarios like graceful shutdowns, worker pool management, and preventing goroutine leaks.
1 parent efbaf15 commit 5572ff0

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
title: 9.4.18 الگو Channel Cancellation
3+
slug: go-concurrency-pattern-channel-cancellation
4+
weight: 177018
5+
mermaid: "true"
6+
---
7+
8+
9+
## 9.4.18.1 توضیحات
10+
11+
الگوی **Channel Cancellation** یا «لغو با کانال» یکی از الگوهای کلیدی در طراحی برنامه‌های همزمان (concurrent) در زبان Go است. این الگو زمانی استفاده می‌شود که نیاز باشد یک یا چند گوروتین را به‌صورت هماهنگ و ایمن متوقف کنیم، به‌ویژه در شرایطی که ادامه اجرای آن‌ها بی‌فایده یا مضر است (مثلاً خطا رخ داده، زمان‌سنج تمام شده یا برنامه در حال خاتمه است). این الگو برخلاف استفاده از `context.Context` (که در Go برای لغو استاندارد توصیه می‌شود)، از یک `channel` اختصاصی برای ارسال سیگنال لغو استفاده می‌کند.
12+
13+
در این الگو، یک کانال معمولاً از نوع `chan struct{}` (یا `chan bool`) تعریف می‌شود که فقط یک بار مقدار می‌گیرد و بعد از آن، تمام گوروتین‌هایی که روی آن منتظر هستند متوجه لغو می‌شوند. گوروتین‌های مصرف‌کننده با استفاده از `select` بررسی می‌کنند که آیا سیگنال لغو دریافت شده یا نه، و در صورت دریافت آن، بلافاصله متوقف می‌شوند. به این ترتیب، سیستم بدون استفاده از متغیرهای مشترک یا قفل (mutex) می‌تواند گوروتین‌های متعدد را متوقف کند.
14+
15+
مزیت اصلی Channel Cancellation در سادگی و سازگاری بالا با سایر کانال‌ها و ساختارهای Go است. این الگو به راحتی در ترکیب با `select` در کنار کانال‌های داده به کار می‌رود، به طوری که هر گوروتین همزمان می‌تواند منتظر داده یا سیگنال لغو باشد. این الگو همچنین مناسب سیستم‌هایی است که به سبک event-driven طراحی شده‌اند یا نیاز دارند از عملیات طولانی یا مسدودکننده (blocking) خارج شوند.
16+
17+
در نهایت، گرچه امروزه استفاده از `context.Context` در اغلب موقعیت‌های لغو توصیه می‌شود، الگوی Channel Cancellation همچنان بسیار مفید، سبک و قابل فهم است—مخصوصاً در کدهایی که ساده‌تر یا فاقد نیاز به توابع context-aware هستند. این الگو پایه‌ای برای پیاده‌سازی shutdown graceful، stop کردن workerها، لغو عملیات IO و کنترل حلقه‌های طولانی‌مدت در برنامه‌های Go محسوب می‌شود.
18+
19+
## 9.4.18.2 دیاگرام
20+
21+
22+
{{< mermaid >}}
23+
sequenceDiagram
24+
participant Main
25+
participant Worker1
26+
participant Worker2
27+
participant CancelChan as Cancel Channel
28+
29+
Main->>Worker1: start with cancel channel
30+
Main->>Worker2: start with cancel channel
31+
32+
Note over Worker1,Worker2: کار ادامه دارد...
33+
34+
Main-->>CancelChan: close(cancel)
35+
36+
Worker1-->>CancelChan: <- cancel
37+
Worker2-->>CancelChan: <- cancel
38+
39+
Worker1->>Main: stopped gracefully
40+
Worker2->>Main: stopped gracefully
41+
{{< /mermaid >}}
42+
43+
44+
45+
46+
## 9.4.18.3 نمونه کد
47+
48+
```go
49+
package main
50+
51+
import (
52+
"fmt"
53+
"time"
54+
)
55+
56+
// تابعی که تا زمان دریافت سیگنال لغو کار می‌کند
57+
func worker(id int, cancel <-chan struct{}) {
58+
for {
59+
select {
60+
case <-cancel:
61+
fmt.Printf("⛔️ Worker %d متوقف شد\n", id)
62+
return
63+
default:
64+
fmt.Printf("⚙️ Worker %d در حال کار...\n", id)
65+
time.Sleep(500 * time.Millisecond)
66+
}
67+
}
68+
}
69+
70+
func main() {
71+
cancel := make(chan struct{}) // کانال لغو مشترک
72+
73+
// اجرای چند گوروتین worker
74+
for i := 1; i <= 3; i++ {
75+
go worker(i, cancel)
76+
}
77+
78+
// اجرای اصلی تا ۲ ثانیه صبر می‌کند
79+
time.Sleep(2 * time.Second)
80+
81+
// ارسال سیگنال لغو با بستن کانال
82+
fmt.Println("📢 ارسال سیگنال لغو به همه گوروتین‌ها...")
83+
close(cancel)
84+
85+
// صبر برای پایان اجرای گوروتین‌ها
86+
time.Sleep(1 * time.Second)
87+
fmt.Println("🏁 پایان برنامه")
88+
}
89+
```
90+
91+
```shell
92+
$ go run main.go
93+
⚙️ Worker 3 در حال کار...
94+
⚙️ Worker 1 در حال کار...
95+
⚙️ Worker 2 در حال کار...
96+
⚙️ Worker 1 در حال کار...
97+
⚙️ Worker 3 در حال کار...
98+
⚙️ Worker 2 در حال کار...
99+
⚙️ Worker 2 در حال کار...
100+
⚙️ Worker 3 در حال کار...
101+
⚙️ Worker 1 در حال کار...
102+
⚙️ Worker 3 در حال کار...
103+
⚙️ Worker 2 در حال کار...
104+
⚙️ Worker 1 در حال کار...
105+
⚙️ Worker 2 در حال کار...
106+
📢 ارسال سیگنال لغو به همه گوروتین‌ها...
107+
⚙️ Worker 3 در حال کار...
108+
⚙️ Worker 1 در حال کار...
109+
⛔️ Worker 3 متوقف شد
110+
⛔️ Worker 2 متوقف شد
111+
⛔️ Worker 1 متوقف شد
112+
🏁 پایان برنامه
113+
```
114+
115+
116+
در این مثال، الگوی **Channel Cancellation** برای متوقف کردن همزمان چند گوروتین به کار گرفته شده است. هدف این الگو آن است که بدون نیاز به متغیرهای اشتراکی یا قفل (mutex)، چند گوروتین را به‌صورت هماهنگ و ایمن متوقف کنیم، آن‌هم با استفاده از یک کانال ساده که نقش سیگنال لغو (cancel signal) را ایفا می‌کند.
117+
118+
در ابتدای برنامه، یک کانال از نوع `chan struct{}` به نام `cancel` تعریف می‌شود. این کانال هیچ داده‌ای حمل نمی‌کند و فقط نقش «علامت توقف» دارد. سپس با استفاده از یک حلقه، سه گوروتین worker راه‌اندازی می‌شوند که همگی به این کانال دسترسی دارند. هر گوروتین درون یک حلقه بی‌نهایت اجرا می‌شود و درون `select` بررسی می‌کند که آیا سیگنال لغو از کانال دریافت شده یا نه. اگر سیگنال لغو دریافت شود (`<-cancel`)، گوروتین با چاپ یک پیام متوقف می‌شود. در غیر این صورت، کار شبیه‌سازی‌شده‌ای انجام می‌دهد و سپس کمی می‌خوابد.
119+
120+
در تابع `main`، برنامه به مدت ۲ ثانیه منتظر می‌ماند تا گوروتین‌ها چند بار پیام «در حال کار» چاپ کنند. سپس کانال `cancel` را می‌بندد (`close(cancel)`) که این عمل به عنوان سیگنال توقف برای تمام گوروتین‌ها عمل می‌کند. در نتیجه، هر گوروتینی که در حالت انتظار روی آن کانال بوده، فعال می‌شود و مسیر لغو را طی می‌کند. پس از آن، `main` کمی صبر می‌کند تا همه گوروتین‌ها فرصت چاپ پیام «⛔️ متوقف شد» را پیدا کنند.
121+
122+
این مثال ساده اما مؤثر نشان می‌دهد که چگونه می‌توان با استفاده از یک کانال مشترک، گوروتین‌های متعدد را به‌صورت هماهنگ متوقف کرد، بدون نیاز به اشتراک‌گذاری متغیر یا وضعیت پیچیده. این الگو در سناریوهای واقعی مانند متوقف‌سازی graceful در سرورها، لغو همزمان چند worker، یا واکنش به خطاهای بحرانی بسیار مفید و پراستفاده است.
123+
124+
## 9.4.18.4 کاربردها
125+
126+
127+
- **خاموش‌سازی گوروتین‌ها:** برای متوقف کردن چند گوروتین به‌صورت هماهنگ هنگام اتمام کار یا دریافت سیگنال خروج.
128+
- **مدیریت worker pool:** برای لغو همه‌ی workerها در صورت خطای بحرانی یا پایان پردازش.
129+
- **جلوگیری از نشت گوروتین (goroutine leak):** برای پایان دادن به گوروتین‌هایی که دیگر نیازی به ادامه کار آن‌ها نیست.
130+
- **کنترل زمان اجرا:** برای قطع عملیات‌های طولانی یا مسدود شده پس از یک مدت مشخص.
131+
- **پیاده‌سازی graceful shutdown:** برای پایان ایمن و منظم سرویس هنگام خاموش شدن یا دریافت سیگنال `SIGINT`.

0 commit comments

Comments
 (0)