Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

جملة Select

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 62 من 107.

تتيح عبارة select لـ goroutine الانتظار على عمليات قنوات (channels) متعددة في وقت واحد. إنها تشبه عبارة switch، ولكن كل حالة (case) تتضمن عملية إرسال أو استقبال عبر القناة. عندما تكون قنوات متعددة جاهزة، تختار select واحدة بشكل عشوائي.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from channel 1" }()
    go func() { ch2 <- "from channel 2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

بدون select، فإن الاستقبال من ch1 سيؤدي إلى الحظر حتى وصول البيانات، مما قد يؤدي إلى فقدان البيانات في ch2. باستخدام select، يتم التعامل مع أي قناة تستقبل البيانات أولاً.

تجعل الحالة default تعليمة select غير حاجبة (non-blocking)—فهي تُنفذ فوراً إذا لم تكن هناك قنوات جاهزة:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

يجمع نمط شائع بين select ومهلات زمنية باستخدام time.After، والذي يُرجع قناة تستقبل قيمة بعد المدة المحددة:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

هذا يمنع برنامجك من الانتظار إلى الأبد عندما قد لا تكتمل عملية القناة أبداً.

challenge icon

التحدي

سهل

لنقم ببناء مراقب خدمة يستخدم عبارة select للتعامل مع مصادر بيانات متعددة متزامنة. سيقوم المراقب الخاص بك بالاستماع إلى قنوات خدمة مختلفة في وقت واحد والاستجابة بشكل مناسب عند وصول البيانات أو عند عدم توفر تحديثات.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • monitor.go: حدد منطق مراقبة الخدمة الخاص بك مع معالجة القنوات القائمة على select.

    قم بإنشاء هيكل ServiceUpdate مع حقول Name (string) و Status (string).

    قم بتنفيذ دالة Monitor تأخذ ثلاث قنوات: primary chan ServiceUpdate، و backup chan ServiceUpdate، و done chan bool. يجب أن تستخدم هذه الدالة select للاستماع إلى جميع القنوات الثلاث في وقت واحد:

    • عند الاستلام من primary: قم بإرجاع السلسلة المنسقة Primary: [Name] is [Status]
    • عند الاستلام من backup: قم بإرجاع السلسلة المنسقة Backup: [Name] is [Status]
    • عند الاستلام من done: قم بإرجاع Monitoring stopped

    قم بتنفيذ دالة CheckStatus تأخذ قناة واحدة updates chan ServiceUpdate وتستخدم select مع حالة default لإجراء فحص غير حاجب (non-blocking):

    • إذا كان التحديث متاحاً: قم بإرجاع Update available: [Name]
    • إذا لم يكن هناك تحديث متاح (الحالة الافتراضية): قم بإرجاع No updates pending
  • main.go: قم بإعداد القنوات وتوضيح سلوك select الحاجب وغير الحاجب.

    اقرأ وضعاً (mode) من الإدخال يحدد السيناريو الذي سيتم تشغيله: primary، أو backup، أو stop، أو check.

    بالنسبة للأوضاع primary و backup و stop: اقرأ أيضاً اسم الخدمة وحالتها. قم بإنشاء القنوات الثلاث، وابدأ goroutine يرسل البيانات إلى القناة المناسبة بناءً على الوضع، ثم استدعِ Monitor واطبع نتيجته.

    بالنسبة لوضع check: اقرأ اسم الخدمة وحالتها، بالإضافة إلى علامة (send أو nosend) تشير إلى ما إذا كان سيتم إرسال البيانات قبل الفحص. قم بإنشاء قناة مخزنة (buffered channel) بسعة 1. إذا كانت العلامة هي send، فأرسل تحديثاً إلى القناة قبل استدعاء CheckStatus. اطبع نتيجة CheckStatus.

سيتم توفير المدخلات التالية:

  • السطر 1: الوضع (primary، أو backup، أو stop، أو check)
  • السطر 2: اسم الخدمة
  • السطر 3: حالة الخدمة
  • السطر 4 (فقط لوضع check): send أو nosend

على سبيل المثال، إذا كان المعطى:

primary
Database
healthy

يجب أن يكون مخرجك:

Primary: Database is healthy

وإذا كان المعطى:

check
Cache
active
nosend

يجب أن يكون مخرجك:

No updates pending

ورقة مرجعية

تسمح عبارة select لـ goroutine بالانتظار على عمليات قنوات (channels) متعددة في وقت واحد. وهي تعمل مثل عبارة switch، ولكن كل حالة (case) تتضمن إرسالاً أو استقبالاً عبر القناة:

select {
case msg1 := <-ch1:
    fmt.Println(msg1)
case msg2 := <-ch2:
    fmt.Println(msg2)
}

عندما تكون قنوات متعددة جاهزة، تختار select واحدة بشكل عشوائي. بدون select، فإن الاستقبال من قناة واحدة سيؤدي إلى الحظر (block) وربما فقدان البيانات في قناة أخرى.

تجعل حالة default عبارة select غير حاظرة (non-blocking) - فهي تُنفذ فوراً إذا لم تكن هناك قنوات جاهزة:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

يجمع نمط شائع بين select والمهلات الزمنية (timeouts) باستخدام time.After، والتي تعيد قناة تستقبل قيمة بعد المدة المحددة:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

هذا يمنع برنامجك من الانتظار للأبد عندما قد لا تكتمل عملية القناة أبداً.

جرّب بنفسك

package main

import (
	"fmt"
)

func main() {
	var mode string
	fmt.Scanln(&mode)

	var serviceName string
	fmt.Scanln(&serviceName)

	var serviceStatus string
	fmt.Scanln(&serviceStatus)

	// TODO: التعامل مع الأوضاع المختلفة: "primary"، "backup"، "stop"، "check"
	
	// للأوضاع "primary"، "backup"، "stop":
	// - إنشاء ثلاث قنوات: primary، backup، done
	// - إطلاق goroutine يرسل البيانات إلى القناة المناسبة
	// - استدعاء Monitor وطباعة النتيجة
	
	// للوضع "check":
	// - قراءة العلم الإضافي ("send" أو "nosend")
	// - إنشاء قناة مخزنة (buffered channel) بسعة 1
	// - إذا كان العلم هو "send"، يتم إرسال تحديث إلى القناة
	// - استدعاء CheckStatus وطباعة النتيجة
	
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming