9.4.12 الگو Monitor

9.4.12 الگو Monitor

9.4.12.1 توضیحات #

الگوی مانیتور به گروتین‌ ها اجازه می‌دهد هنگام ورود به حالت خواب بدون مسدود کردن اجرا یا مصرف منابع، منتظر شرایط خاصی باشند.

حال برای پیاده سازی این الگو ما از ساختاری به نام Cond در پکیج sync کمک میگیریم.

به نقل از ویکی پدیا :

در برنامه‌نویسی همروند (یا همان برنامه‌نویسی موازی)، مانیتور یک ساختار همگام سازی است که به ریسمان ها این امکان را می‌دهد که هم، انحصار متقابل داشته باشند و هم، بتوانند برای یک وضعیت خاص منتظر بمانند (مسدود شوند) تا وضعیت غلط شود. مانیتورها همچنین دارای یک مکانیسم هستند که به ریسمان‌های دیگر، از طریق سیگنال می‌فهمانند که شرایط آنها برآورده شده‌است. یک مانیتور، حاوی یک شئ میوتکس (قفل) و متغیرهای وضعیت است. یک متغیر وضعیت اساساً، ظرفی از ریسمان ها است که منتظر یک وضعیت خاص هستند. مانیتورها برای ریسمان‌ها مکانیسمی را فراهم می‌کنند، تا به‌طور موقت، و با هدف منتظر ماندن برای برآورده شدن شرایط خاص، دسترسی انحصاری را رها کنند، و سپس دسترسی انحصاری را مجدداً به دست آورند و کار خود را از سر گیرند.

9.4.12.2 دیاگرام #

Monitor

9.4.12.3 نمونه کد #

 1package main
 2
 3import (
 4	"fmt"
 5	"sync"
 6)
 7
 8type Item = int
 9
10type Queue struct {
11	items []Item
12	*sync.Cond
13}
14
15func NewQueue() *Queue {
16	q := new(Queue)
17	q.Cond = sync.NewCond(&sync.Mutex{})
18	return q
19}
20
21func (q *Queue) Put(item Item) {
22	q.L.Lock()
23	defer q.L.Unlock()
24	q.items = append(q.items, item)
25	q.Signal()
26}
27
28func (q *Queue) GetMany(n int) []Item {
29	q.L.Lock()
30	defer q.L.Unlock()
31	for len(q.items) < n {
32		q.Wait()
33	}
34	items := q.items[:n:n]
35	q.items = q.items[n:]
36	return items
37}
38
39func main() {
40	q := NewQueue()
41
42	var wg sync.WaitGroup
43	for n := 10; n > 0; n-- {
44		wg.Add(1)
45		go func(n int) {
46			items := q.GetMany(n)
47			fmt.Printf("%2d: %2d\n", n, items)
48			wg.Done()
49		}(n)
50	}
51
52	for i := 0; i < 100; i++ {
53		q.Put(i)
54	}
55
56	wg.Wait()
57}
 1$ go run main.go
 2 1: [ 0]
 3 6: [ 1  2  3  4  5  6]
 4 5: [ 7  8  9 10 11]
 5 4: [12 13 14 15]
 6 3: [16 17 18]
 7 2: [19 20]
 8 9: [21 22 23 24 25 26 27 28 29]
 910: [30 31 32 33 34 35 36 37 38 39]
10 8: [40 41 42 43 44 45 46 47]
11 7: [48 49 50 51 52 53 54]

در کد فوق ما یک صف ساده ایجاد کردیم و این صف را با استفاده از Cond مدیریت کردیم و ۲ چیز در اینجا انجام دادیم :

  • ۱۰ تا گوروتین ایجاد شد و تلاش کردند برای consume آیتم ها در یک ردیف و اگر همه آیتم ها یکبار در دسترس نباشد گوروتین ها منتظر می مانند تا زمانیکه آیتم ها افزایش یابند.
  • گوروتین اصلی صفی که ایجاد کردیم با ۱۰۰ تا آیتم پر میکند و برای هر آیتمی که اضافه می شود یکی از گوروتین هایی که منتظر آیتم ها هستش را بیدار میکند تا فرآیند خود را تکمیل کند.

9.4.12.4 کاربردها #

  • پردازش دسته‌ای: اگر مقدار زیادی داده دارید که باید به صورت دسته‌ای پردازش شوند، می‌توانید از Cond برای سیگنال دادن به زمان آماده شدن یک دسته برای پردازش و پایان پردازش استفاده کنید.
  • انتظار برای رویدادهای خارجی: یک Cond را می توان برای انتظار یک رویداد خارجی، مانند سیگنال از یک سرور راه دور یا تکمیل یک کار پس زمینه استفاده کرد.
  • کنترل جریان (Flow Control): اگر چندین گوروتین دارید که باید هماهنگ شوند تا اطمینان حاصل شود که فقط تعداد معینی از گوروتین ها در یک زمان معین اجرا می شوند، می توانید از Cond برای نشان دادن زمان شروع یا توقف یک گوروتین استفاده کنید.
  • قفل کردن و باز کردن قفل منابع: می‌توانید از یک Cond برای همگام‌سازی دسترسی به منابع مشترک با انتظار برای سیگنال قبل از دریافت قفل و علامت‌گذاری زمانی که قفل آزاد می‌شود، استفاده کنید.
  • همگام سازی چندین گوروتین: وقتی چندین گوروتین دارید که روی یک فرآیند کار می کنند و می خواهید مطمئن شوید که همه آنها قبل از رفتن به کار بعدی تمام می شوند، می توانید از یک Cond برای علامت دادن به پایان هر گوروتین استفاده کنید و منتظر بمانید تا همه قبل از انجام کامل شوند.