0Pricing
Go Academy · บทเรียน

ทิศทางแชนเนลและรูปแบบไปป์ไลน์

แชนเนลสำหรับอ่านอย่างเดียว เขียนอย่างเดียว และไปป์ไลน์

ทิศทางแชนเนลและรูปแบบไปป์ไลน์ เป็นบทเรียน Go Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Go Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

ชนิดช่องสัญญาณแบบกำหนดทิศทาง

Go ช่วยให้คุณจำกัดช่องสัญญาณให้ส่งได้อย่างเดียวหรือรับได้อย่างเดียวในลายเซ็นชนิดข้อมูล วิธีนี้ช่วยบันทึกเจตนาของโค้ดและป้องกันการใช้งานผิด:

package main

// send-only: can only send, not receive or close (close is allowed by sender)
func sender(out chan<- int) {
    out <- 42
}

// receive-only: can only receive, not send or close
func receiver(in <-chan int) int {
    return <-in
}

// bidirectional: can do both
func relay(in <-chan int, out chan<- int) {
    out <- <-in
}

การแปลงทิศทางของช่องสัญญาณ

ช่องสัญญาณแบบสองทิศทางสามารถแปลงโดยนัยเป็นชนิดข้อมูลแบบทิศทางใดทิศทางหนึ่งได้ แต่ไม่สามารถแปลงย้อนกลับได้:

package main
import "fmt"

func produce(out chan<- int) { out <- 1; close(out) }
func consume(in <-chan int)  { fmt.Println(<-in) }

func main() {
    ch := make(chan int)   // bidirectional
    go produce(ch)         // auto-converts to chan<- int
    consume(ch)            // auto-converts to <-chan int
    // chan<- int cannot be converted back to <-chan int
}

ไปป์ไลน์: ขั้นที่ 1 — ตัวสร้าง

ไปป์ไลน์ เชื่อมต่อ goroutine หลายรายการ โดยแต่ละขั้นอ่านค่าจากช่องสัญญาณเอาต์พุตของขั้นก่อนหน้า ตัวสร้างคือลำดับขั้นแรก:

package main
import "fmt"

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func main() {
    ch := generate(2, 3, 4, 5)
    for v := range ch { fmt.Println(v) }
}

ไปป์ไลน์: ขั้นที่ 2 — การแปลง

ขั้นกลางรับค่าจากช่องสัญญาณหนึ่ง แปลงค่า แล้วส่งไปยังอีกช่องสัญญาณหนึ่ง:

package main
import "fmt"

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() { for _, n := range nums { out <- n }; close(out) }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for v := range in { out <- v * v }
        close(out)
    }()
    return out
}

func main() {
    for v := range square(generate(2, 3, 4, 5)) {
        fmt.Println(v)  // 4, 9, 16, 25
    }
}

ไปป์ไลน์: ตัวอย่างแบบสามขั้นตอนครบถ้วน

เชื่อมต่อสามขั้นตอน ได้แก่ generate, แปลง และกรอง:

package main
import "fmt"

func gen(nums ...int) <-chan int {
    c := make(chan int)
    go func() { for _, n := range nums { c <- n }; close(c) }()
    return c
}

func double(in <-chan int) <-chan int {
    c := make(chan int)
    go func() { for v := range in { c <- v * 2 }; close(c) }()
    return c
}

func evens(in <-chan int) <-chan int {
    c := make(chan int)
    go func() { for v := range in { if v%2 == 0 { c <- v } }; close(c) }()
    return c
}

func main() {
    for v := range evens(double(gen(1,2,3,4,5))) {
        fmt.Println(v)  // 2, 4, 6, 8, 10
    }
}

การยกเลิกไปป์ไลน์ด้วยช่องสัญญาณ done

ส่งช่องสัญญาณ done ไปด้วย เพื่อให้แต่ละขั้นของไปป์ไลน์ออกจากการทำงานก่อนเวลาได้เมื่อถูกยกเลิก:

package main
import "fmt"

func gen(done <-chan struct{}, nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            select {
            case out <- n:
            case <-done:  // cancel signal
                return
            }
        }
    }()
    return out
}

func main() {
    done := make(chan struct{})
    ch := gen(done, 1, 2, 3, 4, 5)
    fmt.Println(<-ch)  // 1
    close(done)        // cancel remaining work
    fmt.Println("pipeline cancelled")
}

การกระจายงาน: แบ่งงาน

การกระจายงานจะแจกจ่ายงานจากช่องสัญญาณหนึ่งไปยัง goroutine หลายรายการเพื่อประมวลผลพร้อมกัน:

package main
import ("fmt"; "sync")

func fanOut(in <-chan int, workers int) []<-chan int {
    outs := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        out := make(chan int)
        outs[i] = out
        go func() {
            for v := range in { out <- v * v }  // each worker processes
            close(out)
        }()
    }
    return outs
}

func main() {
    fmt.Println("fan-out distributes work to multiple goroutines")
    _ = sync.WaitGroup{}
}

การรวมช่องสัญญาณ: รวมช่องสัญญาณ

Fan-in รวมช่องสัญญาณหลายช่องให้เป็นหนึ่งช่อง:

package main
import ("fmt"; "sync")

func merge(cs ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    for _, c := range cs {
        wg.Add(1)
        c := c
        go func() { defer wg.Done(); for v := range c { out <- v } }()
    }
    go func() { wg.Wait(); close(out) }()
    return out
}

func gen(nums ...int) <-chan int {
    c := make(chan int)
    go func() { for _, n := range nums { c <- n }; close(c) }()
    return c
}

func main() {
    for v := range merge(gen(1,2), gen(3,4), gen(5,6)) {
        fmt.Println(v)
    }
}

แนวทางปฏิบัติที่ดีสำหรับไปป์ไลน์

หลักการออกแบบไปป์ไลน์:

  • แต่ละขั้นตอน: โกโรทีนอ่านจาก <-chan เขียนไปยัง chan<- และปิดช่องสัญญาณขาออกเมื่อทำงานเสร็จ
  • มีเพียงผู้ผลิตเท่านั้นที่ปิดช่องสัญญาณ — ห้ามให้ผู้บริโภคปิด
  • ส่งช่องสัญญาณ done (หรือบริบท) เพื่อยกเลิกการทำงาน
  • ใช้ชนิดช่องสัญญาณแบบระบุทิศทางในลายเซ็นของฟังก์ชันทั้งหมด
  • อ่านค่าที่ค้างอยู่ในช่องสัญญาณเมื่อยกเลิก เพื่อป้องกันโกโรทีนรั่วไหล

ไปป์ไลน์ในโลกจริง: การประมวลผล CSV

ไปป์ไลน์เหมาะอย่างยิ่งกับการประมวลผลข้อมูล — อ่านบรรทัด แยกวิเคราะห์ กรอง และแปลงข้อมูล:

package main
import ("fmt"; "strings")

func splitLines(data string) <-chan string {
    out := make(chan string)
    go func() {
        for _, line := range strings.Split(data, "\n") {
            if line != "" { out <- line }
        }
        close(out)
    }()
    return out
}

func main() {
    csv := "alice,30\nbob,25\ncarol,35"
    for line := range splitLines(csv) {
        parts := strings.Split(line, ",")
        fmt.Printf("name=%s age=%s\n", parts[0], parts[1])
    }
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ชนิดช่องสัญญาณแบบระบุทิศทางในลายเซ็นของฟังก์ชันมีจุดประสงค์อะไร

ทบทวน: ทิศทางของช่องสัญญาณและไปป์ไลน์

สรุป:

  • chan<- T ใช้ส่งข้อมูลเท่านั้น ส่วน <-chan T ใช้รับข้อมูลเท่านั้น — ระบุเจตนาไว้อย่างชัดเจนและมีความปลอดภัยที่ตรวจสอบได้ขณะคอมไพล์
  • ไปป์ไลน์: เชื่อมโกโรทีนเป็นลำดับ โดยแต่ละโกโรทีนอ่านจากช่องสัญญาณหนึ่งช่องและเขียนไปยังอีกช่องหนึ่ง
  • Fan-out: ช่องสัญญาณขาเข้าเดียว → โกโรทีนหลายตัว
  • Fan-in: ช่องสัญญาณหลายช่อง → รวมเป็นช่องเดียว
  • ปิดช่องสัญญาณจากฝั่งผู้ส่งเสมอ และใช้ช่องสัญญาณ done เพื่อยกเลิกการทำงาน

คำถามที่พบบ่อย

บทเรียน “ทิศทางแชนเนลและรูปแบบไปป์ไลน์” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ทิศทางแชนเนลและรูปแบบไปป์ไลน์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Go Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ทิศทางแชนเนลและรูปแบบไปป์ไลน์”

แชนเนลสำหรับอ่านอย่างเดียว เขียนอย่างเดียว และไปป์ไลน์ คุณปฏิบัติ Go Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Go Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Go Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ทิศทางแชนเนลและรูปแบบไปป์ไลน์” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Go Academy นี้ได้ไหม

ได้ บทเรียน Go Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การเริ่มใช้กอรูทีน
  2. แชนเนลแบบไม่มีบัฟเฟอร์
  3. แชนเนลแบบมีบัฟเฟอร์
  4. ทิศทางแชนเนลและรูปแบบไปป์ไลน์
← กลับไปที่ Go Academy