Bir olay döngüsü, optimize edilmiş yoklama ile sadece bir / while döngüsü midir?


55

Bir olay döngüsünün ne olduğunu anlamaya çalışıyorum. Genelde açıklama, bir olay döngüsünde, bir olayın gerçekleştiği bildirilene kadar bir şey yaptığınızdır. Daha sonra olayı idare edin ve daha önce ne yaptığınızı yapmaya devam edin.

Yukarıdaki tanımı bir örnekle eşlemek için. Bir olay döngüsünde 'dinleyen' bir sunucum var ve bir soket bağlantısı tespit edildiğinde, içindeki veriler okunuyor ve görüntüleniyor, ardından sunucu daha önce olduğu gibi devam ediyor / dinlemeye başlıyor.


Bununla birlikte, bu olay oluyor ve bize “aynen böyle” bildiriliyor, benim için halletmem çok önemli. Şöyle diyebilirsiniz: "Öyle değil" olay dinleyicisini kaydetmeniz gerekiyor. " Fakat olay dinleyicisi nedir, fakat bir nedenden dolayı geri dönmeyen bir fonksiyondur. Bir etkinlik olduğunda bildirilmeyi bekleyen kendi döngüsünde mi? Etkinlik dinleyicisi de bir etkinlik dinleyicisini kaydetmeli mi? Nerede bitiyor?


Olaylar, çalışmak için iyi bir soyutlamadır, ancak sadece bir soyutlamadır. Sonunda oylamanın kaçınılmaz olduğuna inanıyorum. Belki de kodumuzda yapmıyoruz, ancak alt seviyeler (programlama dili uygulaması veya işletim sistemi) bizim için yapıyor.

Temelde, yeterince düşük bir yerde çalışan, bu nedenle meşgul beklemeyle sonuçlanmayan sözde koda iniyor:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Bu benim bütün fikri anladığım ve bunun doğru olup olmadığını duymak istiyorum. Bütün fikrimin temelde yanlış olduğunu kabul etmeye açığım, bu durumda doğru açıklamayı istiyorum.


3
Olay sistemleri, Gözlemci modelinin uygulamalarıdır . Deseni anlamak, olayları anlamanızı sağlamalıdır. Yoklama gerekli değildir.
Steven Evers

1
Sonunda evet, dil yapıları soyut olsa bile.
GrandmasterB

@SteveEvers Wiki bağlantınızda. Ne olduğunu EventSourcedeğil yoklama klavye giriş varsa yapıyor?
TheMeaningfulEngineer 18:13

@Alan: Herhangi bir şey yapıyor olabilir, ancak özellikle klavye girişi için, uygulamanızı klavye olaylarını dinlemek olarak kaydetmek için özel API'leriniz vardır; bu sayede, yoklama içermeyen etkinlik kaynağınız olarak kullanabilirsiniz. Tabii ki, yeterince aşağı inerseniz, USB her zaman sorgulama yapar, ancak kesilen bir PS / 2 klavye kullandığımızı varsayalım ve ardından sıfır sorgulama olaylarına dayanan bir klavye giriş yığınınız var.
Phoshi

Yıllar boyunca bu soruları yaşadım, ama neden sormak için hiç uğraşmadığımı söyleyemem. Teşekkürler, şimdi @Karl Bielefeldt'in beni aydınlattığı anlayışından memnunum.
18'de 05.03'de 0xc0de

Yanıtlar:


54

Olay olayları hazır değilse, çoğu olay döngüsü askıya alınır; bu, işletim sisteminin bir olay gerçekleşene kadar herhangi bir yürütme süresi vermeyeceği anlamına gelir.

Etkinliğin basılmakta olan bir anahtar olduğunu söyleyin. İşletim sisteminde bir tuşa basıldığını kontrol eden bir döngü olup olmadığını sorabilirsiniz. Cevap hayır. Basılan tuşlar , donanım tarafından asenkron olarak kullanılan bir kesme oluşturur . Aynı şekilde zamanlayıcılar, fare hareketleri, gelen bir paket vb.

Aslında, çoğu işletim sistemi için olaylara oy verme soyutlamadır. Donanım ve işletim sistemi olayları eşzamansız olarak işler ve bunları uygulamalar tarafından sorgulanabilecek bir sıraya koyar. Yalnızca gömülü sistemlerdeki donanım düzeyinde gerçek sorgulamayı görürsünüz ve hatta her zaman değil.


4
Bir kesinti teldeki voltaj değişikliğini değil mi? Bu bir olayı kendi başına tetikleyebilir mi, yoksa voltaj değeri için pimi yoklamalı mıyız?
TheMeaningfulEngineer 18:13

8
Bu kendiliğinden bir olayı tetikleyebilir. İşlemci bu şekilde tasarlanmıştır. Aslında bir işlemci uykudan bir kesme ile uyandırılabilir.
Karl Bielefeldt

2
Açıklama ile kafam karıştı Most event loops will block. Bu, "iş parçacığı kullanmak yerine, olay döngüsü paradigması, engelleme yapmayan asenkron çağrıları kullanır" ile nasıl uyumludur?
TheMeaningfulEngineer 18:13

1
GTK + gibi bir kütüphane için olay döngülerine bakarsanız, yeni olayları kontrol ederler, ardından döngüdeki olay işleyicilerini çağırırlar, ancak herhangi bir olay yoksa, semafor veya zamanlayıcı veya başka bir şey üzerinde engellerler. Bireysel geliştiriciler, boş bir olay kuyruğunda engellenmeyen, ancak yaygın olarak kullanılan kütüphanelerin tümü engelleyen kendi olay döngülerini yapar. Olay döngüleri, aksi halde çok verimsizdir.
Karl Bielefeldt

1
Sanırım bu şekilde bakabilirsin, ama uygulama kodu yerine kütüphanede ve / veya işletim sisteminde çok iş parçacığı. Olay döngüleri, uygulama geliştiricisinin senkronizasyon sorunlarını ortadan kaldırır.
Karl Bielefeldt

13

Bir olay dinleyicisini kendi döngüsünü çalıştıran bir işlev olarak değil, ilk silahı başlangıç ​​silahını bekleyen röle yarışı olarak düşünüyorum. Yoklama yerine olayları kullanmanın önemli bir nedeni, CPU çevrimleriyle daha verimli olmalarıdır. Neden? Donanımdan yukarıya bakın (aşağı kaynak kodu yerine).

Bir Web sunucusu düşünün. Sunucunuz çağırdığında listen()ve engellediğinde, kodunuz bir röle koşucusu olarak yerini alıyor. Yeni bir bağlantının ilk paketi geldiğinde, ağ kartı işletim sistemini keserek yarışı başlatır. İşletim sistemi paketi tutan bir kesme hizmeti yordamı (ISR) çalıştırıyor. ISR, batonu, bağlantıyı kuran daha üst düzey bir rutine geçirir. Bağlantı canlı olduktan sonra, bu yordam batonu ve batonu listen()kodunuza geçirir. Bu noktada, bağlantı ile ne istersen yapabilirsin. Hepimizin bildiği gibi, yarışlar arasında her röle koşucusu bara gidebilirdi. Olay soyutlamasının bir gücü, kodunuzun bilmesi ya da bakımı gerektirmemesidir.

Bazı işletim sistemleri yarışın bir bölümünü çalıştıran olay kodunu içerir, copu serbest bırakır, ardından bir sonraki yarışın başlamasını beklemek için başlangıç ​​noktasına geri döner. Bu anlamda, olay işleme, çok sayıda eşzamanlı döngüde sorgulama optimize edilmiştir. Bununla birlikte, süreci başlatan her zaman bir dış tetik vardır. Olay dinleyicisi, geri dönmeyen bir işlev değil, çalışmadan önce bu dış tetikleyiciyi bekleyen bir işlevdir. Ziyade:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Bunu şöyle düşünüyorum:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

ve signalişleyicinin bir sonraki çalıştırılışında ve arasında , kavramsal olarak kod çalışan veya döngü yok.


Bu mantıklı, ancak kesmeyi beklerken olay döngüsünün ne işi var? Engelleniyorsa, olay döngüsünün karşısındaki paradigma olan 'çok dişli' yaklaşım bu değil mi?
TheMeaningfulEngineer

Kodunuz döngü içeriyorsa, forever: { select(); do stuff; }o zaman her döngüden tekrar yarışa girersiniz. Bunu tek bir iplikten defalarca veya ayrı dişler veya işlemciler üzerinde paralel olarak yapsanız da, her olayı kendi ırkı olarak düşünüyorum. Örneğin, bir Web tarayıcısı ayrı iş parçacıklı çoklu olay döngülerine sahip, bir kullanıcı arayüzü için en az bir ve indirdiği her sayfa için bir tane olan çok iş parçacıklı bir programdır. Kodlama yaparken sorduğum soru "olayları yeterince hızlı nasıl işlerim?" Bazen cevap bir döngüdür, bazen iplikler, sıklıkla bir kombinasyondur.
cxw

Birkaç on yıldan fazla bir süredir olaya dayalı programlama yapıyorum ve bu cevabı çok kafa karıştırıcı buldum. Bir dinleyicinin çalışması için, bir olayı bekleyecek bir döngü olması ve ardından onu kayıtlı tüm dinleyicilere yönlendirmesi gerekir. (Donanım kesintileri yerine yazılımdan bahsettiğimizi varsayalım)
Bryan Oakley

8

Hayır. "Optimize edilmiş yoklama" değildir. Bir olay döngüsü, sorgulama yerine kesme odaklı G / Ç kullanır.

Süre, Kadar, For vb. Döngüler sorgulama döngüleridir.

"Yoklama" bir şeyi tekrar tekrar kontrol etme işlemidir. Döngü kodu sürekli yürütür ve yana çünkü o küçük, "sıkı" döngü işlemci görevleri geçin ve başka bir şey yapmak için çok az zaman vardır. Neredeyse tüm "kilitlenmeler", "donmalar", "kilitlenmeler" veya bilgisayar yanıt vermediğinde ne demek istediğinizi sormak, istenmeyen bir sorgulama döngüsünde sıkışmış kodun tezahürüdür. Enstrümantasyon% 100 CPU kullanımı gösterecektir.

Kesintiye dayalı olay döngüleri, yoklama döngülerinden çok daha verimlidir. Yoklama CPU döngülerinin son derece israf edici bir kullanımıdır;

Bununla birlikte, kod kalitesini optimize etmek için, çoğu dil, bir programdaki işlevsel olarak benzer amaçlara hizmet ettikleri için, sorgulama döngüsü paradigmasını mümkün olduğu kadar olay gönderme komutları için mümkün olduğunca kullanmaya çalışır. Bu nedenle, yoklama, bir tuşa basmayı veya başka bir şeyi beklemenin daha bilinen bir yolu olarak, deneyimsizlerin kullanması ve kendi başına iyi çalışabilecek bir programla kurması kolaydır, ancak çalışırken hiçbir şey işe yaramaz. Makineyi "devraldı".

Diğer cevaplarda açıklandığı gibi, kesme odaklı olay tesliminde, temel olarak CPU içinde bir "bayrak" ayarlanır ve bu bayrak başka bir işlem (klavye gibi) tarafından değiştirilinceye kadar işlem "askıya alınır" (çalıştırılamaz) sürücü, kullanıcı bir tuşa bastığında bunu değiştiriyor). Bayrak, bir çizgi "yüksek çekilme" gibi gerçek bir donanım koşuluysa, buna "kesme" veya "donanım kesme" denir. Ancak çoğu, CPU'da veya ana bellekte (RAM) yalnızca bir bellek adresi olarak uygulanır ve "semafor" olarak adlandırılır.

Semaforlar yazılım kontrolü altında değiştirilebilir ve böylece yazılım işlemleri arasında çok hızlı, basit bir sinyalleşme mekanizması sağlayabilir.

Ancak, kesintiler ancak donanım tarafından değiştirilebilir. Kesintilerin en yaygın kullanımı, iç saat çipinin düzenli aralıklarla tetiklediğidir. Saat kesintileriyle harekete geçen sayısız yazılım eyleminden biri semaforların değişmesidir.

Çok fazla şey bıraktım ama bir yerde durmak zorunda kaldım. Lütfen daha fazla ayrıntıya ihtiyacınız olup olmadığını sorun.


7

Tipik olarak cevap, donanımdır, işletim sistemi ve kontrol etmediğiniz arkaplan konuları, zahmetsiz görünmesini sağlamak için anlaşılmaz. Ağ kartı , CPU'yu bilgilendirmek için bir kesintiye neden olan bazı verileri alır . İşletim sisteminin kesme işleyicisi bunu gerçekleştirir. Ardından, kontrol etmediğiniz bir arka plan iş parçacığı (olaya kaydolarak oluşturulmuş ve etkinliğe kaydolduğunuzdan beri uyuyor) arka plan iş parçacığı, etkinlik tarafından ele alınmasının bir parçası olarak işletim sistemi tarafından uyandırılır ve etkinlik işleyicinizi çalıştırır.


7

Şimdiye kadar gördüğüm tüm cevaplara karşı çıkıp "evet" diyeceğim . Bence diğer cevaplar işleri çok zorlaştırıyor. Kavramsal açıdan tüm olay döngüleri esasen şöyledir:

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

Olay döngülerini ilk kez anlamaya çalışıyorsanız, bunları basit bir döngü olarak düşünmek zarar vermez. Altta yatan bazı çerçeveler, işletim sisteminin bir etkinlik sunmasını beklemekte, ardından olayı bir veya daha fazla işleyiciye yönlendirmekte, ardından bir sonraki olayı beklemektedir, vb. Bir uygulama yazılımı perspektifinden gerçekten hepsi bu kadar.


1
Basit olması için +1. Ancak vurgu “beklemek” üzerine; Bu kodun çalıştırılması döngünün ilk satırında DURDURACAK ve bir olay meydana gelinceye kadar devam etmeyecektir. Bu, bir olay gerçekleşene kadar art arda kontrol eden yoğun bir yoklama döngüsünden farklıdır.
Tom Panning

1

Tüm olay tetikleyicileri döngüler içinde ele alınmaz. Kendi etkinlik motorlarımı sık sık yazma biçimim şu şekilde olurdu:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

Not 1'in bir döngüde olmasına rağmen, döngünün her dinleyiciyi bilgilendirmek için olduğunu unutmayın. Olay tetikleyicisinin kendisi mutlaka bir döngüde değildir.

Teoride, işletim sistemi düzeyinde anahtar olaylar aynı tekniği kullanabilir (sanırım sık sık sorgulama yaptıklarını mı düşünüyorsunuz? Burada sadece spekülasyon yapıyorum) registerListener.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.