Menu
Coddy logo textTech

Kanallar ve İletişim

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 60 / 107.

Kanallar, Go'nun goroutine'ler arası iletişim için birincil mekanizmasıdır. Belleği doğrudan paylaşmak yerine, goroutine'ler Go'nun "Belleği paylaşarak iletişim kurmayın; iletişim kurarak belleği paylaşın." felsefesini takip ederek kanallar aracılığıyla değer gönderir ve alır.

make fonksiyonunu ve chan anahtar kelimesini kullanarak bir kanal oluşturun:

messages := make(chan string)

Değer göndermek ve almak için ok operatörünü <- kullanın. Ok, veri akışının yönünü gösterir:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "hello"  // kanala gönder
    }()
    
    msg := <-messages  // kanaldan al
    fmt.Println(msg)   // Output: hello
}

Varsayılan olarak, gönderimler ve alımlar diğer taraf hazır olana kadar bloklanır. Goroutine "hello" gönderdiğinde, main almaya hazır olana kadar bekler. Bu bloklama davranışı yerleşik senkronizasyon sağlar; time.Sleep kullanmaya gerek yoktur.

Artık daha fazla değer gönderilmeyeceğini belirtmek için bir kanalı kapatabilirsiniz. Alıcılar bunu tespit edebilir:

close(messages)

msg, ok := <-messages
if !ok {
    fmt.Println("Channel closed")
}

Kanallar tiplidir; bir chan string yalnızca dizgileri taşıyabilir. Bu tip güvenliği, goroutine'lerin doğru veri tipleriyle iletişim kurmasını sağlayarak hataları çalışma zamanı yerine derleme zamanında yakalar.

challenge icon

Görev

Kolay

Goroutine'lerin kanallar aracılığıyla nasıl iletişim kurduğunu gösteren bir mesaj aktarma sistemi kuralım. Mesajların her biri eşzamanlı olarak çalışan farklı işleme aşamaları arasında aktığı bir boru hattı (pipeline) oluşturacaksınız.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • relay.go: Mesaj işleme boru hattınızı tanımlayın.

    ID (int) ve Content (string) alanlarına sahip bir Message struct'ı oluşturun.

    Boru hattınızın farklı aşamalarını temsil eden üç fonksiyon uygulayın:

    • Producer(messages []Message, out chan Message) - Bir mesaj dilimi (slice) alır ve her birini çıktı kanalına gönderir. Tüm mesajları gönderdikten sonra, tamamlandığını bildirmek için kanalı kapatın.
    • Transformer(in chan Message, out chan Message) - Giriş kanalından mesajları alır, her mesajın içeriğini büyük harfe dönüştürür ve dönüştürülen mesajı çıktı kanalına gönderir. Giriş kanalı kapandığında (iki değerli alma formu kullanılarak tespit edilir), çıktı kanalını kapatın.
    • Consumer(in chan Message) []string - Giriş kanalından tüm mesajları alır ve bunları formatlanmış dizelerden oluşan bir dilimde toplar. Her dize şu formatı izlemelidir: Message [ID]: [Content]. Kanal kapandığında dilimi döndürün.
  • main.go: Boru hattını kurun ve eşzamanlı mesaj akışını yönetin.

    Mesaj sayısını okuyun, ardından her mesajın ID'sini ve içeriğini okuyun. Mesajları oluşturun ve üç boru hattı aşamanızı birbirine bağlamak için iki kanal kurun. Producer ve Transformer'ı goroutine olarak başlatın, ardından sonuçları toplamak için Consumer'ı ana goroutine'de çalıştırın. Her sonucu ayrı bir satıra yazdırın.

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: Mesaj sayısı (tam sayı)
  • Sonraki satırlar: Her mesaj için iki satır - mesaj ID'si (tam sayı), ardından içeriği (dize)

Örneğin, şu girdi verildiğinde:

2
1
hello world
2
go channels

Çıktınız şu olmalıdır:

Message 1: HELLO WORLD
Message 2: GO CHANNELS

Boru hattı şu şekilde akar: Producer mesajları gönderir → Transformer büyük harfe dönüştürür → Consumer sonuçları toplar ve formatlar. Her ok, eşzamanlı aşamaları birbirine bağlayan bir kanalı temsil eder. Büyük harf dönüşümü için strings paketini kullanın.

Kopya kağıdı

Kanallar (Channels), Go'nun goroutine'ler arasındaki iletişim için temel mekanizmasıdır. chan anahtar kelimesiyle birlikte make kullanarak bir kanal oluşturun:

messages := make(chan string)

Değer göndermek ve almak için ok operatörünü <- kullanın. Ok, veri akışının yönünü gösterir:

messages <- "hello"  // kanala gönder
msg := <-messages    // kanaldan al

Varsayılan olarak, gönderme ve alma işlemleri diğer taraf hazır olana kadar bloklanır (block), bu da yerleşik bir senkronizasyon sağlar:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "hello"  // kanala gönder
    }()
    
    msg := <-messages  // kanaldan al
    fmt.Println(msg)   // Output: hello
}

Artık daha fazla değer gönderilmeyeceğini belirtmek için bir kanalı kapatın. Alıcılar, iki değerli alma formunu kullanarak kapanışı tespit edebilirler:

close(messages)

msg, ok := <-messages
if !ok {
    fmt.Println("Channel closed")
}

Kanallar tiplidir; bir chan string yalnızca dizeleri (string) taşıyabilir, bu da derleme zamanı tip güvenliği sağlar.

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Mesaj sayısını oku
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// Mesajları oku
	messages := make([]Message, n)
	for i := 0; i < n; i++ {
		idLine, _ := reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(idLine))
		contentLine, _ := reader.ReadString('\n')
		content := strings.TrimSpace(contentLine)
		messages[i] = Message{ID: id, Content: content}
	}

	// TODO: İş hattı (pipeline) aşamalarını bağlamak için iki kanal oluşturun

	// TODO: Producer'ı bir goroutine olarak başlatın

	// TODO: Transformer'ı bir goroutine olarak başlatın

	// TODO: Consumer'ı ana goroutine'de çalıştırın ve sonuçları toplayın

	// TODO: Her bir sonucu ayrı bir satıra yazdırın
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler