A eşyordam Bir iş parçacığına benzer, kendi yığını, kendi yerel değişkenleri ve talimatlar için kendi işaretçisi olan bir yürütme satırıdır, ancak genel değişkenleri ve diğer öğeleri diğer eşyordamlarla paylaşma özelliği vardır.
Ama arada farklar olduğunu açıklığa kavuşturmalıyız. İş Parçacığı ve eşyordamlar, temel fark, iş parçacığı kullanan bir programın bunları aynı anda çalıştırmasıdır, eşyordamlar Öte yandan, eşyordamları kullanan bir programın bunlardan yalnızca birini çalıştırdığı ve bunların askıya alınmasının yalnızca açıkça talep edildiğinde elde edildiği işbirlikçidirler.
NS eşyordamlar Son derece güçlüler, bu kavramın neleri kapsadığını ve bunları programlarımızda nasıl kullanabileceğimizi görelim.
Temel konseptler
içindeki eşyordamlarla ilgili tüm işlevler Lua fonksiyonun bulunduğu eşyordam tablosunda bulunur. oluşturmak () onları yaratmamıza izin verir, basit bir argümanı vardır ve eşyordamın çalışacağı kodlu fonksiyondur, burada dönüşü yeni eşyordamı temsil eden iş parçacığı türünün bir değeridir. Eşyordamı oluşturma argümanı bile bazen aşağıdaki örnekte olduğu gibi anonim bir işlevdir:
co = coroutine.create (işlev () yazdır ("Merhaba Solvetic") sonu)A eşyordam dört farklı durumu olabilir:
- askıya alındı
- aceleyle
- ölü
- normal
Oluşturduğumuzda, durumda başlar durdurulan, bu, eşyordamın ilk kez oluşturulduğunda otomatik olarak çalışmadığı anlamına gelir. Bir eşyordamın durumuna aşağıdaki şekilde danışılabilir:
yazdır (coroutine.status (co))Eşyordamımızı nerede çalıştırabiliriz, sadece işlevini kullanmamız gerekir. özetler(), dahili olarak yaptığı şey, durumunu askıya alınmış durumdan çalışıyor duruma getirmektir.
coroutine.özgeçmiş (co)Tüm kodlarımızı bir araya getirip, yaptıktan sonra koroutinimizin ek durumunu sorgulamak için ek bir satır eklersek özetler geçtiği tüm durumları görebiliriz:
co = coroutine.create (işlev () yazdır ("Merhaba Solvetic") bitiş) yazdır (co) yazdır (eşyordam.status (co)) eşyordam.özgeçmiş (co) yazdır (eşyordam.status (co))Terminalimize gidip örneğimizi çalıştırıyoruz, programımızın çıktısını görelim:
lua coroutines1.lua iş parçacığı: 0x210d880 Askıya Alındı Hello Solvetic öldüGördüğümüz gibi, eşyordamın ilk izlenimi ipliğin değeridir, o zaman durumumuz var. askıya alındı, ve bu iyidir, çünkü bu, oluştururken ilk durum olduğundan, ardından özetler İletiyi yazdırdığı eşyordamı çalıştırıyoruz ve bundan sonra durumu ölümisyonunu yerine getirmiş gibi.
İlk bakışta eşyordamlar, işlevleri çağırmanın karmaşık bir yolu gibi görünebilir, ancak bundan çok daha karmaşıktırlar. Aynı şeyin gücü, işlevin büyük bir bölümünde yatmaktadır. teslim olmak () Çalışmakta olan bir eşyordamı daha sonra yeniden başlatmak için askıya almaya izin veren bu işlevin kullanımına bir örnek görelim:
co = coroutine.create (fonksiyon () için i = 1.10 do print ("özgünleştirme eşyordamı", i) eşyordam.verim () son uç) eşyordam.özgeçmiş (eş) eşdüzenli.özgeçmiş (birlikte) eşyordam.özgeçmiş (eş ) eşyordam .özgeçmiş (co)Bu işlevin yapacağı şey, ilk işleve kadar çalıştırılır. teslim olmakve bir döngümüz olup olmadığına bakılmaksızın için, sadece pek çok kişiye göre yazdıracak özetler Eşyordamımızı yapalım, bitirmek için çıktıyı terminalden görelim:
lua eşyordamları 1. lua 1 2 3 4Bu terminalden çıkış olacaktır.
Filtreler
Eşyordamları açıklayan en açık örneklerden biri şudur: tüketici Y jeneratör bilginin. Diyelim ki bir dosyayı okumaktan sürekli olarak bazı değerler üreten bir fonksiyonumuz var ve sonra bunları okuyan başka bir fonksiyonumuz var, bu fonksiyonların nasıl görünebileceğine dair açıklayıcı bir örnek görelim:
işlev üreteci () while true do yerel x = io.read () gönder (x) bitiş bitiş işlev tüketici () iken doğru do yerel x = alma () io.write (x, "\ n") bitiş bitişBu örnekte hem tüketici hem de jeneratör herhangi bir dinlenme olmadan çalışır ve işlenecek daha fazla bilgi olmadığında onları durdurabiliriz, ancak buradaki sorun, fonksiyonların nasıl senkronize edileceğidir. Göndermek() Y almak(), çünkü her birinin kendi döngüsü vardır ve diğerinin çağrılabilir bir hizmet olduğu varsayılır.
Ancak eşyordamlarla bu sorun, çift işlev kullanılarak hızlı ve kolay bir şekilde çözülebilir. özgeçmiş / verim fonksiyonlarımızı sorunsuz bir şekilde çalıştırabiliriz. Bir eşyordam işlevi çağırdığında teslim olmak, yeni bir işlev girmez, ancak bekleyen ve yalnızca özgeçmiş kullanılarak bu durumdan çıkabilen bir çağrı döndürür.
Aynı şekilde arama yaparken özetler yeni bir işlev de başlatmaz, bir bekleme çağrısı döndürür. teslim olmak, bu süreci özetlemek, işlevlerini senkronize etmek için ihtiyacımız olan şeydir. Göndermek() Y almak(). Bu işlemi uygularsak kullanmamız gerekir almak() Uygulamak özetler yeni bilgileri oluşturmak için jeneratöre ve ardından Göndermek() uygulamak teslim olmak Tüketici için, fonksiyonlarımızın yeni değişikliklerle nasıl göründüğüne bakalım:
işlev alma () yerel durum, değer = eşyordam.özgeçmiş (oluşturucu) dönüş değeri bitiş işlevi gönderme (x) eşyordam.yield (x) bitiş gen = eşyordam.create (işlev () true do yerel x = io.read () gönder (x) bitiş sonu)Ama yine de programımızı daha da geliştirebiliriz ve bu, filtrelerAynı anda hem üretici hem de tüketici olarak işlev gören görevler olan , çok ilginç bir bilgi dönüşüm süreci yaratıyor.
A filtre yapabilir özetler bir jeneratörden yeni değerler almak ve ardından uygulamak teslim olmak tüketici için verileri dönüştürmek. Bir önceki örneğimize nasıl kolayca filtre ekleyebileceğimizi görelim:
gen = üreteç () fil = filtre (gen) tüketici (fil)Gördüğümüz gibi, son derece basitti, programımızı optimize etmenin yanı sıra, gelecekteki bakım için önemli olan okunabilirlik konusunda puanlar kazandık.
Yineleyici olarak corroutines
Jeneratör/tüketicinin en net örneklerinden biri, yineleyiciler Bir yineleyicinin özyinelemeli döngü içinde vücut tarafından tüketilecek bilgileri ürettiği özyinelemeli döngülerde bulunur, bu nedenle bu yineleyicileri yazmak için eşyordamları kullanmak mantıksız olmaz, eşyordamların bile bu görev için özel bir aracı vardır.
yapabileceğimizi göstermek için eşyordamlar, belirli bir dizinin permütasyonlarını oluşturmak için bir yineleyici yazacağız, yani bir dizinin her elemanını son konuma yerleştirip çevireceğiz ve sonra kalan elemanların tüm permütasyonlarını yinelemeli olarak üreteceğiz, bakalım nasıl yapacağımızı görelim. orijinal işlev, eşyordamları içermez:
işlev print_result (var) for i = 1, #var do io.write (var [i], "") end io.write ("\ n") endŞimdi yaptığımız şey bu süreci tamamen değiştirmek, önce print_sonuç () verim ile, değişimi görelim:
function permgen (var1, var2) var2 = var2 veya # var1 eğer var2 <= 1 ise coroutine.yield (var1) yoksaBu, yineleyicilerin nasıl çalıştığını gösteren açıklayıcı bir örnektir, ancak Lua adı verilen bir işlev sağlar paketlemek hangisine benzer oluşturmakAncak, bir eşyordam döndürmez, çağrıldığında bir eşyordamı özetleyen bir işlev döndürür. Daha sonra kullanmak paketlemek sadece aşağıdakileri kullanmalıyız:
işlev izinleri (var) dönüş coroutine.wrap (işlev () permgen (var) bitiş) bitişGenellikle bu işlevi kullanmak, kullanmaktan çok daha kolaydır. oluşturmak, bize tam olarak ihtiyacımız olanı verdiği için, yani özetlemek gerekirse, bununla birlikte oluşturulan eşyordamın durumunu doğrulamamıza izin vermediğinden daha az esnektir. paketlemek.
içindeki eşyordamlar Lua El ele yürütülmesi gereken ancak bilgiyi sağlayanın tamamlanmasını bekleyen süreçlerle ilgili her şeyle başa çıkmak için son derece güçlü bir araçtır, ayrıca jeneratör / tüketici süreçleriyle ilgili karmaşık sorunları çözmek için kullanımlarını görebiliriz. ve ayrıca programlarımızda yineleyicilerin yapısını optimize etmek.