الانتقال إلى المحتوى الرئيسي
الوحدة 7 · الدرس 3 من 533/50 في الدورة~12 min
دروس الوحدة (3/5)

`تحديد`: تعدد القنوات

select ينتظر عمليات القنوات المتعددة في نفس الوقت. إنها switch لتزامن Go: فهي تتيح لك التفاعل مع أول قناة جاهزة بين العديد.

بناء الجملة

Go
select {
case v := <-ch1:
    fmt.Println("ch1:", v)
case v := <-ch2:
    fmt.Println("ch2:", v)
case ch3 <- 99:
    fmt.Println("inviato su ch3")
case <-time.After(time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("nessuno è pronto")
}

دلالات:

  • يتم تقييم جميع الحالات. يتم اختيار حالة واحدة فقط من الحالات الجاهزة.
  • إذا كانت هناك عدة حالات جاهزة، يكون الاختيار عشوائيًا زائفًا (بدون ترتيب).
  • إذا لم تكن هناك حالة جاهزة: يتم حظر select حتى تصبح الحالة جاهزة.
  • إذا كان هناك default: فسيتم تشغيله فورًا عندما لا تكون هناك حالة جاهزة (**حدد **غير محظور**).

نمط المهلة

Go
select {
case res := <-fetch(url):
    use(res)
case <-time.After(2 * time.Second):
    log.Println("timeout: fetch troppo lento")
}

time.After(d) تُرجع <-chan Time التي تنتج قيمة بعد d. إنها الطريقة الكلاسيكية لربط الانتظار.

تحديد عدم الحظر بشكل افتراضي

Go
select {
case msg := <-ch:
    process(msg)
default:
    // niente da fare, prosegui
}

إنه "يستطلع" القناة دون حجبها. مفيد في مسارات التعليمات البرمجية التي يجب أن يظل مستجيبًا دون انتظار إلزامي.

نمط "تم القناة".

ادمج select مع قناة الإلغاء:

Go
func worker(in <-chan Job, done <-chan struct{}) {
    for {
        select {
        case j := <-in:
            process(j)
        case <-done:
            return       // chi gestisce done segnala lo shutdown
        }
    }
}

close(done) من مشغلات المنسق case <-done في كل روتين الانتظار: الإغلاق المنسق.

حلقة مع التحديد

Go
for {
    select {
    case v := <-input:
        if v == nil {
            return
        }
        handle(v)
    case <-time.After(5 * time.Second):
        keepAlive()
    case <-ctx.Done():
        return
    }
}

نمط "حلقة الحدث" النموذجي للخوادم والعاملين.

قناة لا شيء كحالة "معطلة".

خدعة متقدمة: case <-ch مع ch == nil أبدًا مختارة. يتيح لك "تعطيل" الحالة ديناميكيًا:

Go
var in chan int = source()
for {
    select {
    case v, ok := <-in:
        if !ok {
            in = nil    // disabilita questo case
            continue
        }
        handle(v)
    case <-ctx.Done():
        return
    }
}

عندما يتم إغلاق in، أقوم بإلغائه: يستمر select في التعامل مع فقط حالات أخرى.

جربه

تمرين#go.m7.l3.e1
المحاولات: 0جارٍ التحميل…

تنفيذ تحديد مع حالتين تستقبلان من ch1 و ch2؛ طباعة أي واحد وصل أولا.

جارٍ تحميل المحرر…
إظهار التلميح

بناء الجملة `select { case ...: ... }` بحالة واحدة لكل قناة.

الحل متاح بعد 3 من المحاولات

تمرين#go.m7.l3.e2
المحاولات: 0جارٍ التحميل…

أضف حالة انتهاء المهلة إلى التحديد باستخدام time.After(time.Second).

جارٍ تحميل المحرر…
إظهار التلميح

`time.After(d)` هي قناة تستقبل بعد d.

الحل متاح بعد 3 من المحاولات

Quiz#go.m7.l3.e3
جاهز

ما الذي يجعل التحديد غير محظور؟

Go
select { case v := <-ch: ... ??? }
خيارات الإجابة

خلاصة

  • select ينتظر أول حالة جاهزة من بين العديد من عمليات القناة.
  • الاختيار العشوائي الزائف بين حالات جاهزة متعددة.
  • default → تحديد غير محظور (الاقتراع).
  • time.After(d) للمهلات (ضع في اعتبارك التسرب في الحلقات: استخدم context).
  • الأنماط: قناة منتهية، حلقة حدث، قناة صفرية لتعطيل الحالة.
  • فارغ select{} = كتلة أبدية: خطأ دائمًا تقريبًا.