Go Programlama Dili
  • KİTAP HAKKINDA
    • GİRİŞ
    • Katkıda Bulunanlar
  • BÖLÜM 1 (Ön Eğitim)
    • Golang Hakkında
    • Sıkça Sorulan Sorular
    • Go Derleyicisi Kurulumu
    • VSCode Go Eklentisi Yükleme
    • Go Projesi Oluşturma
    • Merhaba Dünya
    • VSCode Varsayılan Hata Ayıklayıcıyı Seçme
    • Farklı Platformlara Build (İnşa) Etme
    • Klasör Build Etme
    • Paketler
    • Yorum Satırı
    • Veri Tipleri
    • Aritmetik Operatörler
    • İlişkisel Operatörler
    • Mantıksal Operatörler
    • Atama Operatörleri
    • Değişkenler ve Atanması
    • Sabitler
    • Kod Gruplama İşlemi
    • Tür Dönüşümü
    • Bölüm 1 Sınav
  • BÖLÜM 2 (İşlem Yapma)
    • Fonksiyonlar
    • Fonksiyon Çeşitleri
    • Anonim Fonksiyonlar
    • Boş Tanımlayıcılar
    • Döngüler
    • If-Else
    • Switch
    • Defer
    • Pointers (İşaretçiler)
    • Bölüm 2 Sınav
  • BÖLÜM 3 (Gruplama)
    • Struct
    • Anonim Struct'lar
    • Struct Fonksiyonlar (Methodlar)
    • Diziler (Arrays)
    • Dilimler (Slices)
    • Range
    • Map
    • Interface
    • Dinamik Değişkenler
    • Generics
  • BÖLÜM 4 (Eş zamanlılık)
    • Goroutine
    • Kanallar (Channels)
    • Anonim Goroutine Fonksiyonlar
    • WaitGroup ile Asenkron İşlemleri Beklemek
    • Mutex ile Asenkron İşlem Sırası
    • Zamanlayıcılar (Tickers)
    • Select
  • BÖLÜM 5
    • init() Fonksiyonu (Ön Yükleme)
    • Import (Kütüphane Ekleme) Yöntemleri
    • Dışa Aktarma (Exporting)
    • Print Fonksiyonu Birkaç İnceleme
    • Format ve Kaçış Karakterleri
    • Çok Satırlı String Oluşturma
    • Sprintf
    • Golang'te Kullanıcıdan Giriş Alma
    • Testing (Test Etme)
    • Panic & Recover
  • BÖLÜM 6 (Paketler)
    • Strings
    • os/exec (Komut Satırına Erişim)
    • Komut Satırı Argümanları (Args)
    • Komut Satırı Bayrakları (Flags)
    • os/signal
    • Sort (Sıralama)
    • Strconv (String Çeviri)
    • Log (Kayıt)
    • Paket (Kütüphane) Yazmak
    • Regexp (Kurallı İfadeler)
  • BÖLÜM 7 (Dosya İşlemleri)
    • Çapraz Platform Dosya Yolları
    • İşletim Sistemini Görme
    • Dosya Varlığı Kontrolü
    • ioutil ile Dosya Okuma ve Yazma
    • Bir Dizindeki Dosya ve Klasörleri Sıralama
    • XML Parsing (Ayrıştırma)
    • JSON Parsing (Ayrıştırma)
      • Dinamik JSON Parsing Yöntemleri
    • ini Dosyası Okuma ve Düzenleme
    • Web Scrapper (goquery)
    • chromedp (Web Driver)
  • VERİTABANI
    • sqlite3
    • MySQL
    • MongoDB
    • GORM
  • Web Sunucu (Server), Ağ İşlemleri
    • net/http ile Web Server Oluşturma
    • HTML Şablonlar (Templates)
    • Statik Kütüphanesi ile Dosyaları Uygulamaya Gömme
    • Gin Web Kütüphanesi
      • Gin Dosya Yükleme
    • gRPC
    • Heroku'da Go Uygulaması Yayınlama
    • HTTP İstekleri (Requests)
    • RabbitMQ
      • RabbitMQ Kurulumu
      • Basitçe RabbitMQ Kullanımı
  • GUI (Grafiksel Kullanıcı Arayüzü)
    • WebView
    • notify (Bildirim)
  • Mikro-denetleyiciler
    • Gobot ile Arduino Yanıp-Sönen LED Yapımı
    • Tinygo ile Küçük Yerler için Golang
  • CLI
    • 🐍Cobra CLI
  • ⚙️Yapılandırma
    • 🐍Viper
  • Pratik Bilgiler
    • Go Geliştiricileri için Makefile
    • Derleme (Build) Detayını Görme
    • Visual Studio Code için Golang Özelleştirmeleri
  • 👾PLUS
    • Uber Go Stil Kılavuzu
Powered by GitBook
On this page

Was this helpful?

  1. BÖLÜM 4 (Eş zamanlılık)

Mutex ile Asenkron İşlem Sırası

PreviousWaitGroup ile Asenkron İşlemleri BeklemekNextZamanlayıcılar (Tickers)

Last updated 4 years ago

Was this helpful?

Size konu başlığını şöyle açıklayayım. Örneğin bir banka uygulaması para çekme ve yatırma gibi özelliklere sahiptir. Programlama mantığında para yatırmak ve çekmek için mevcut para miktarını bilmemiz gerekir. Banka uygulamasının mantığı en basit derecede bu şekilde çalışır.

Banka hesabımızda asenkron işlem yapıldığını varsayalım. Yani bir hesaptan aynı anda birden fazla kullanıcı işlem yapıyor olsun.

Örneğin hesabımızda 100₺ olsun. Birinci kullanıcı 20₺ yatırsın. Aynı anda ikinci kullanıcı 50₺ çeksin. Bu iki kullanıcınında kullandığı program işlem yapmaya başladığında önce para miktarını alıyor. Daha sonra yapılacak işleme göre ya ekleme ya da çıkarma işlemi yapıyor. Fakat birden fazla kullanıcı aynı anda bu işlemi yaparsa hesaptaki parada yanlışlık olacaktır.

Basit bir görsel ile inceleyelim.

İşlemlere aynı anda başlandığını varsayalım.

Bu işlemin sonuncunda hangi kullancının işlemi sonuncu olarak biterse para miktarı onun sonucu olur. Yani kullanıcı 2'nin işlemi kullanıcı 1'den sonra biterse yeni para miktarı 50₺ olur.

Bu gibi örneklerde asenkron işlemlere sıra verilmesi gerekir. Mutex tam olarak bu işi yapıyor. Bunun için bir Mutex nesnesi oluşturuyoruz. İşlemlerimizi bu nesne üzerinden yapıyoruz. Bu nesne aynı anda sadece bir işlemi gerçekleştiriyor. Bu yüzden sıra işlemi sağlıyor. Önce başlayan asenkron işlem ilk sırada oluyor. Tamamlanınca diğerine sıra geçiyor. Böyle düşündüğümüz zaman "bunun senkron programlamadan ne farkı var?" diyebilirsiniz. Farkı asenkron fonksiyonların içindeki istediğimiz kısımları senkron çalıştırmamız.

Örnek bir para yatırma-çekme uygulaması yazalım. İşlemin sağlık çalışması için, para miktarıyla aynı anda sadece bir kişi işlem yapabilmelidir.

package main

import (
	"fmt"
	"sync" // mutex'i kullanmak için
)
//global olarak mutex nesnesi oluşturalım.
var mt sync.Mutex


func paraÇek(bakiye *float64, çekilecekMiktar float64, wg *sync.WaitGroup) {
	/*
	* mt isimli mutex'i bu işlem yapılırken kilitliyoruz.
	* bu sayede mt mutex'ini başka işlemler kullanamıyor.
	*/
	mt.Lock()

	/*
	bu kısımda asenkron olmasını istemediğimiz işlemi yapalım.
	*/
	*bakiye -= 15
	fmt.Printf("Yeni Bakiye: %.2f\n", *bakiye)

	/*
	* diğer işlemlerinde kullanabilmesi için mutex'i tekrardan açalım.
	* mt mutex açılınca diğer asenkron işlemdeki mt mutex'i çalışmaya başlar. 
	*/
	mt.Unlock()
	fmt.Println("Çekme işlemi tamamlandı.")

	/*
	* waitgroup ile işlemin tamamlandığını belirttik.
	* böylece wg havuzu 2'den 1'e düştü
	*/
	wg.Done()
}

//bu fonksiyonda yukarıdaki ile aynı mantıkta
func paraYatır(bakiye *float64, yatırılacakMiktar float64, wg *sync.WaitGroup) {
	mt.Lock()
	*bakiye += 65
	fmt.Printf("Yeni Bakiye: %.2f\n", *bakiye)
	mt.Unlock()
	fmt.Println("Yatırma işlemi tamamlandı.")
	wg.Done()
}

func main() {

	/*
	* asenkron işlemlerimizin, ana iş parçacığında tamamlanmasını
	* beklemek için waitgroup nesnesi oluşturalım
	*/
	var wg sync.WaitGroup

	//2 fonksiyonu da bekleyeceğimiz için Add'e 2 yazalım
	wg.Add(2)

	//fonksiyonlarımızın kullancağı bakiye değişkenimiz
	var bakiye float64 = 100
	fmt.Printf("İlk Bakiye: %.2f\n", bakiye)
	
	/*
	* paraÇek ve paraYatır fonksiyonlarımızı aynı anda başlatıyoruz.
	* hangisi daha önce başlarsa mutex sırasına ilk o girer. bu esnada diğer
	* fonksiyon mutex'in açılmasını bekler.
	*/
	go paraÇek(&bakiye, 25, &wg)
	go paraYatır(&bakiye, 65, &wg)

	/*
	* ana iş parçacığı tamamlandığında asenkron çalışan fonksiyonları beklemez.
	* beklemediğinde de asenkron fonksiyonlar çalışmadan program sonlanır.
	* ana iş parçacığının asenkron işlemleri beklemesi için waitgroup sonucunun 0 olmasını bekleriz.
	* wg.Add(2) yazarak 2 adet wg.Done() fonksiyonu çalıştığında wg.Add(0) olur ve
	* wg.Wait() tamamlanır ve program başka işlemler yapılmıyor ise sonlanır.
	*/
	wg.Wait()
}

Çıktımız aşağıdaki gibi olacaktır.

İlk Bakiye: 100.00 Yeni Bakiye: 165.00 Yatırma işlemi tamamlandı. Yeni Bakiye: 150.00 Çekme işlemi tamamlandı.

Yukarıdaki çıktıya göre, paraYatır() fonksiyonu paraÇek() fonkisyonundan önce çalışmıştır.

Örnek asenkron işlem