Structured Concurrency Nedir?

Structured Concurrency

Bu makalede concurrency’nin getirdiği karmaşıklığa ve ilgili terimlere değineceğim. Ardından 2015 senesinden itibaren ortaya çıkan, özellikle Kotlin’in concurrency yapısında yer edinmiş olan Structured Concurrency kavramından bahsedeceğim.

Program yazarken uyguladığımız computation türlerini sequential ve concurrent olarak ikiye ayırabiliriz. Kullandığımız dilde threading veya herhangi async bir yapı kullanmıyorsak, yukardan aşağıya mantığıyla kodlarımızı yazıyorsak bu koda sequential diyebiliriz. Diğer yandan birçok işi aynı anda yapabilen bir program istiyorsak bunu da parallelism veya concurrency ile halledebiliriz.

Şimdi de structured ve unstructured kavramlarını karşlılaştıralım.

Unstructured programlamaya en basitinden Assembly dilini örnek verebiliriz. Kodun entry ve exit noktaları belli değil (yeşil oklar goto statement’ı temsil etmektedir):

https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/

gcc compiler ile bir C++ kodu assembly’e dönüştürebilir ve makine diline çok yakın bir kod elde ederiz. Baktığımızda oldukça kafa karıştırıcı hatta spagetti diyebileceğimiz bir koda dönüşüyor.

Structured programlamada ise genel olarak şu statementlar bulunur:

ve bunlarla şu şekilde bir program üretebiliriz:

Yukardaki diyagramda bir statement içerisinde bir bug bulduğumuzu düşünelim ve bug’ı çözmek için o statement’a odaklanalım. Bug’ı çözerken farkettiyseniz diğer statement’lara bakmamıza gerek bile yok, ordan oraya zıplayan goto statement’ları yok, sadece tek bir entry ve tek bir exit noktası var, herşey o statement içerisinde. İlgili yerde yapmamız gerekeni yapıp direkt çıkabiliriz. Yani her bir modülü diğer modüllerden bağımsız bir şekilde inceleyebiliyoruz. Bu olay structured programlamanın en büyük avantajlarından biridir.

Peki makale başlığı olan structured concurrency nedir?

Çok kısa özet geçmem gerekirse structured programming + concurrency = structured concurrency diyebiliriz.

Peki konuyu biraz açalım.

Dillerin concurrency yapısını incelediğinizde baya bi low-level olduğunu görürsünüz, bu da yanında concurrency buglarının kolay oluşması, bu bugları fixlerken trace etmenin zor olması, ardından non-concurrency buglara sıra gelmesi ve bazı gizli kalmış bugların bulunması gibi birçok işimizi zorlaştıran yönü bulunmaktadır. Bu da bizi yazılımın reliability‘sine sürüklemektedir. Hardware’lar güçlendikçe ve fiyatları düştükçe yazılımların ücreti artmakta, ücreti arttıkça da daha karmaşık hâle gelmekte ve çözüme daha dirençli buglar’ın oluşması olasılığını artırmaktadır.

Bu duruma karşı birkaç yaklaşım ortaya konulmuştuır. Hem daha reliable kodların yazılmasını sağlamak hem de bir yazılımcının ürettiği koddaki bug oranının azaltılması amaçlanmıştır. Structured concurrency de bunlardan biridir.

Normal fonksiyonlar ile asenktron fonksiyonları bir karşılaştıralım.

regular functionsasynchronous functions
arguments
return value
throw exception
executioninlinesomewhere else
cancellable
ownercaller
lifetimeless than caller
https://skoppe.github.io/dconf-2022/19

Structured concurrency’de:

  • Her bir async birimin bir owner’ı olmalı
  • Owner, sahip olduğu async birimlerin hepsinden uzun yaşamalı
  • Async birimlerde oluşan hatalar owner’a yönlendirilmeli
  • Owner, sahip olduğu async birimleri cancel edebilmeli
  • Async birimler cancel işleminin tamamlandığı sinyalini bir şekilde verebilmeli (başarılı, hatalı, exception, işlem tamam, vs.)

Python ve AnyIO

Python’da asenkron yapıyı destekleyen builtin asyncio’nun yanısıra Trio ve Curio(yakın zamanda deprecate oldu) asenkron yapı kurmaya yarayan kütüphaneler bulunmaktadır. İhtiyaçlardan olan bir taskın hata verdiğinde diğer taskların iptal edilmesi gibi bir yapı asyncio içerisinde bulunmadığından bu ihtiyaçları diğer kütüphaneler gidermektedir, bunun sebebi de biraz Trio ile birlikte nursery kavramı komünite içerisinde geniş yer buldu.

AnyIO kütüphanesi ise asyncio etrafında sarmalanmış bir structured concurrency kütüphanesidir. AnyIO’yu Trio’nun devamı olarak, daha iyi bir yapıya kavuşturulmuş şeklinde düşünebiliriz. Hem Trio uygulamaları hem asyncio üzerinde rahatlıkla kullanılabilmektedir. Exception handling için sinyal yapıları gibi bulunmaktadır. Bahsetmiş olduğumuz Trio’daki nursery kavramı AnyIO içerisinde TaskGroup şeklinde geçmektedir.

Pytohn3.11 ile TaskGroup kavramı da asyncio içerisinde yer etmiş bulunmakta. Kotlin’de ilk başta bu mantıkla yazılan ve daha sonra standart library yapılan coroutine kütüphanesiyle benimsendi. Golang içerisinde concurrency yapısı (goroutine) bu mantıkla yazıldı, Swift ve ardından Java’ya da bu mantıkla yazılım yapılabilecek elementler eklendi.

Kaynaklar:
Structured concurrency in Python with AnyIO
DConf ’22: Structured Concurrency — Sebastiaan Koppe
Structured concurrency and pure functions
Reasoning about asyncio.Semaphore (Guido blog)