Menu
Coddy logo textTech

Tamponlu ve Tamponsuz Kanallar

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

Şu ana kadar kullandığımız kanallar tamponsuzdur (unbuffered)—değer tutma kapasiteleri yoktur. Bir gönderme işlemi, başka bir goroutine alana kadar bloklanır ve bunun tersi de geçerlidir. Tamponlu kanallar (buffered channels), dahili depolama alanı ekleyerek gönderme işlemlerinin anında bir alıcı olmadan tamamlanmasına olanak tanır.

make fonksiyonuna bir kapasite ileterek tamponlu bir kanal oluşturun:

// Tamponsuz - kapasite 0
unbuffered := make(chan int)

// Tamponlu - kapasite 3
buffered := make(chan int, 3)

Tamponlu bir kanal ile, gönderimler yalnızca tampon dolu olduğunda engellenir ve alımlar yalnızca tampon boş olduğunda engellenir:

ch := make(chan string, 2)

ch <- "first"   // bloklamaz - tamponda yer var
ch <- "second"  // bloklamaz - tamponda yer var
// ch <- "third" bloklar - tampon dolu

fmt.Println(<-ch)  // "first"
fmt.Println(<-ch)  // "second"

Tamponda şu anda kaç öğe olduğunu kontrol etmek için len(ch), toplam kapasite için ise cap(ch) kullanın.

Her birinin ne zaman kullanılacağı: Tamponsuz kanallar (unbuffered channels) sıkı bir senkronizasyon sağlar; gönderici, alıcının değeri aldığını bilir. Tamponlu kanallar (buffered channels), gönderici ve alıcı zamanlamasını birbirinden ayırır; bu, üreticilerin ve tüketicilerin farklı hızlarda çalıştığı durumlarda yararlıdır. Ancak, tamponlu kanallar senkronizasyon hatalarını gizleyebilir, bu nedenle tamponlama için özel bir nedeniniz yoksa tamponsuz olanları tercih edin.

challenge icon

Görev

Kolay

Tamponlanmış (buffered) ve tamponlanmamış (unbuffered) kanallar arasındaki farkı gösteren bir toplu işlemci (batch processor) oluşturalım. Üreticinin (producer) öğeleri bir işlemciye gönderdiği, zamanlamalarını birbirinden ayırmak ve üreticinin önceden çalışmasına izin vermek için tamponlanmış kanalların kullanıldığı bir sistem oluşturacaksınız.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • processor.go: Kanalları kullanarak toplu işlem mantığınızı tanımlayın.

    ID (int) ve Value (string) alanlarına sahip bir Item yapısı (struct) oluşturun.

    İki fonksiyon uygulayın:

    • Producer(items []Item, out chan Item) - Her bir öğeyi çıktı kanalına gönderir. Her öğeyi gönderdikten sonra şunu yazdırın: Produced item [ID]. Tüm öğeler gönderildikten sonra, len(out) kullanarak tamponda bekleyen mevcut öğe sayısını şu formatta yazdırın: Buffer has [count] items. Ardından kanalı kapatın.
    • Consumer(in chan Item) []string - Giriş kanalından öğeleri alır ve bunları formatlanmış dizelerden (strings) oluşan bir dilim (slice) içinde toplar. Alınan her öğe için dize şu şekilde olmalıdır: Consumed: [ID] - [Value]. Kanal kapatıldığında dilimi döndürün.
  • main.go: Tamponlanmış kanalı kurun ve üretici ile tüketiciyi koordine edin.

    Tampon kapasitesini, ardından öğe sayısını ve son olarak her öğenin ID'sini ve değerini okuyun. Belirtilen kapasiteye sahip tamponlanmış bir kanal oluşturun. Tamponu doldurmak için önce Üreticiyi (Producer) çalıştırın (bir goroutine olarak değil), ardından tüm öğeleri işlemek için Tüketiciyi (Consumer) çalıştırın. Tüketilen her sonucu ayrı bir satıra yazdırın.

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

  • Satır 1: Tampon kapasitesi (tam sayı)
  • Satır 2: Öğe sayısı (tam sayı)
  • Sonraki satırlar: Her öğe için iki satır - öğe ID'si (tam sayı), ardından değeri (dize)

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

3
3
1
apple
2
banana
3
cherry

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

Produced item 1
Produced item 2
Produced item 3
Buffer has 3 items
Consumed: 1 - apple
Consumed: 2 - banana
Consumed: 3 - cherry

3 birimlik bir tampon kapasitesiyle, üreticinin engellenmeden (blocking) 3 öğenin tamamını nasıl gönderebildiğine ve tüketici başlamadan önce tamponun bekleyen 3 öğeyi nasıl gösterdiğine dikkat edin. Eğer tampon öğe sayısından daha küçük olsaydı, üretici yer açılmasını beklerken engellenecekti.

Kopya kağıdı

Go'daki kanallar tamponsuz (kapasite 0) veya tamponlu (dahili depolama ile) olabilir.

Kanalları make ile oluşturun:

// Tamponsuz - kapasite 0
unbuffered := make(chan int)

// Tamponlu - kapasite 3
buffered := make(chan int, 3)

Tamponlu kanal davranışı:

  • Gönderimler yalnızca tampon dolu olduğunda engellenir
  • Alımlar yalnızca tampon boş olduğunda engellenir
ch := make(chan string, 2)

ch <- "first"   // engellenmez - tamponda yer var
ch <- "second"  // engellenmez - tamponda yer var
// ch <- "third" engellenirdi - tampon dolu

fmt.Println(<-ch)  // "first"
fmt.Println(<-ch)  // "second"

Tampon durumunu kontrol edin:

  • len(ch) - şu anda tamponda bulunan öğe sayısı
  • cap(ch) - toplam tampon kapasitesi

Ne zaman kullanılır:

  • Tamponsuz: Sıkı senkronizasyon—gönderici, alıcının değeri aldığını bilir
  • Tamponlu: Farklı hızlarda çalıştıklarında gönderici ve alıcı zamanlamasını birbirinden ayırın

Kendin dene

package main

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

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

	// Tampon kapasitesini oku
	line, _ := reader.ReadString('\n')
	bufferCapacity, _ := strconv.Atoi(strings.TrimSpace(line))

	// Öğe sayısını oku
	line, _ = reader.ReadString('\n')
	numItems, _ := strconv.Atoi(strings.TrimSpace(line))

	// Her bir öğenin kimliğini ve değerini oku
	items := make([]Item, numItems)
	for i := 0; i < numItems; i++ {
		line, _ = reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(line))
		line, _ = reader.ReadString('\n')
		value := strings.TrimSpace(line)
		items[i] = Item{ID: id, Value: value}
	}

	// YAPILACAK: Belirtilen kapasiteye sahip tamponlu bir kanal oluşturun

	// YAPILACAK: Tamponu doldurmak için Üreticiyi (goroutine olarak değil) çalıştırın

	// YAPILACAK: Tüm öğeleri işlemek için Tüketiciyi çalıştırın

	// YAPILACAK: Tüketilen her 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