Neden programlama dilleri senkron / asenkron problemi otomatik olarak yönetmiyor?


27

Bununla ilgili çok fazla kaynak bulamadım: Eşzamanlı olmayan bir şekilde eşzamansız kod yazabilmenin mümkün / iyi bir fikir olup olmadığını merak ediyordum.

Örneğin, bir veritabanında depolanan kullanıcı sayısını (eşzamansız bir işlem) alan bazı JavaScript kodları:

getNbOfUsers(function (nbOfUsers) { console.log(nbOfUsers) });

Böyle bir şey yazabilmek güzel olurdu:

const nbOfUsers = getNbOfUsers();
console.log(getNbOfUsers);

Ve böylece derleyici yanıtı beklemeyi otomatik olarak halleder ve ardından yürütür console.log. Sonuçların başka bir yerde kullanılmasından önce, zaman uyumsuz işlemlerin tamamlanmasını bekler. Geri arama vaadi vaatlerini daha az kullanırız, zaman uyumsuzdur / bekler ya da her neyse, ve bir işlemin sonucunun derhal mevcut olup olmadığına asla endişelenmek zorunda kalmazdık.

Hatalar hala nbOfUsersdene / yakala veya Swift dilinde olduğu gibi seçeneklerle kontrol edilebilir ( bir tamsayı veya hata mı aldı?) .

Mümkün mü? Bu korkunç bir fikir / ütopya olabilir ... Bilmiyorum.


58
Sorunu gerçekten anlamıyorum. Eğer "her zaman eşzamansız işlemi bekler", o zaman eşzamansız bir işlem değildir, bu eşzamanlı bir işlemdir. Açıklayabilir misin? Belki de aradığınız davranış tipinin bir tanımını verebilirsiniz? Ayrıca, "Bu konuda ne düşünüyorsunuz" Yazılım Mühendisliği konusunda konu dışı . Sorunuzu somut bir problem bağlamında, tek, kesin, kanonik, nesnel olarak doğru bir cevabı olan formüle etmeniz gerekir.
Jörg W Mittag

4
@ JörgWMittag Ben kesinlikle dönüştürmek için awaitsa saran bir varsayımsal C # hayalTask<T>T
Caleth

6
Teklif ettiğin şey mümkün değil. Sonucu beklemek ya da ateş etmek ve unutmak isteyip istemediğinize karar vermek derleyici değildir. Veya arka planda çalıştırın ve daha sonra bekleyin. Neden kendini böyle kısıtlıyorsun?
garip

5
Evet, çok kötü bir fikir. Yalnızca async/ awaitişlevini kullanın, bu işlem yürütmenin zaman uyumsuz bölümlerini açık hale getirir.
Bergi

5
İki şeyin eşzamanlı olarak gerçekleştiğini söylediğinizde, bu şeylerin herhangi bir sırayla gerçekleşmesinin doğru olduğunu söylüyorsunuz. Kodunuz, hangi yeniden siparişlerin kodunuzun beklentilerini ihlal etmeyeceğini açıklığa kavuşturmazsa, bunları eşzamanlı yapamaz.
Rob

Yanıtlar:


65

Async / await, iki ek anahtar kelimeyle de olsa, önerdiğiniz otomatik yönetimdir. Neden önemlidir? Geriye doğru uyumluluk dışında mı?

  • Bir koroinin askıya alınabileceği ve sürdürülebileceği açık noktalar olmadan, beklenen bir değerin nerede beklendiğini tespit etmek için bir tip sisteme ihtiyacımız olacaktır . Birçok programlama dili böyle bir sisteme sahip değildir.

  • Açık bir değeri bekliyor yaparak, birinci sınıf nesneler olarak beklenen değerleri de geçebiliriz: sözler. Bu, yüksek dereceli kod yazarken süper yararlı olabilir.

  • Eşzamansız kod, dilin özel durumlarının bulunmamasına veya bulunmamasına benzer şekilde, bir dilin yürütme modeli için çok derin etkilere sahiptir. Özellikle, bir zaman uyumsuz işlev yalnızca zaman uyumsuz işlevler tarafından beklenebilir. Bu, tüm arama işlevlerini etkiler! Peki ya bu bağımlılık zincirinin sonunda bir işlevi eşzamansızdan eşzamansız olarak değiştirirsek? Bu, geriye dönük uyumsuz bir değişiklik olur… tüm işlevler zaman uyumsuz olmadıkça ve her işlev çağrısı varsayılan olarak beklenmiyorsa.

    Ve bu oldukça istenmeyen bir durum çünkü performansı çok kötü. Sadece ucuz değerleri iade edemezsiniz. Her işlev çağrısı çok daha pahalı hale gelirdi.

Eşzamansız harika, ancak bir tür örtülü eşzamans gerçekte işe yaramaz.

Haskell gibi saf işlevsel diller bir kaçış hattına sahiptir çünkü yürütme sırası büyük ölçüde belirtilmemiş ve gözlemlenemez. Veya farklı şekilde ifade edilir: herhangi bir işlem sırası açıkça kodlanmalıdır. Gerçek dünyadaki programlar için, özellikle de asenkron kodun çok iyi olduğu G / Ç-ağır programları için oldukça zahmetli olabilir.


2
Tipik bir sisteme ihtiyacınız yoktur. ECMAScript, Smalltalk, Self, Newspeak, Io, Ioke, Seph gibi Şeffaf Futures'lar, tyoe sistemi veya dil desteği olmadan kolayca uygulanabilir. Smalltalk ve soyundan, bir nesne kimliğini şeffaf bir şekilde değiştirebilir, ECMAScript'te şeklini şeffaf bir şekilde değiştirebilir. Futures'ları şeffaf hale getirmek için tek ihtiyacınız olan şey, zaman uyumsuzluk için dil desteğine gerek yok.
Jörg W Mittag

6
@ JörgWMittag Ne dediğinizi ve bunun nasıl işe yarayacağını anlıyorum, ancak tür sistemi olmayan şeffaf gelecekler aynı anda birinci sınıf geleceklere sahip olmayı zorlaştırıyor, değil mi? Geleceğe veya geleceğin değerine mesaj göndermek isteyip istemediğimi, tercihen someValue ifItIsAFuture [self| self messageIWantToSend]jenerik kodla bütünleşmenin zor olmasından daha iyi bir şeyi seçmek için bir yola ihtiyacım var.
öğleden

8
@ amon "Async kodumu vaatler ve vaatler monadlar olarak yazabilirim." Monad'lar aslında burada gerekli değil. Thunks aslında sadece vaat ediyor. Haskell'deki neredeyse tüm değerler kutuya eklendiğinden Haskell'deki neredeyse tüm değerler zaten vaat ediyor. Bu yüzden parsaf Haskell koduyla hemen hemen her yere atıp paralellik kazanabilirsiniz.
DarthFennec

2
Async / await, devam monadini hatırlatıyor.
les

3
Aslında, hem istisnalar hem de asenkron / bekleyen, cebirsel etkilerin örnekleridir .
Alex Reinking

21

Eksik olan şey, asenkron operasyonların amacı : Bekleme sürenizi kullanmanızı sağlar!

Bir sunucudan bir miktar kaynak istemek gibi, bir eşzamanlı işleme, yanıtı derhal ve hemen bekleyerek eşzamanlı bir işleme dönüştürürseniz , iş parçanız bekleme süresinden başka bir şey yapamaz . Sunucunun yanıt vermesi 10 milisaniyeyi alırsa, atık için yaklaşık 30 milyon CPU döngüsü vardır. Yanıtın gecikmesi, isteğin yürütme süresi olur.

Programcıların zaman uyumsuzluk işlemleri icat etmelerinin tek nedeni , doğası gereği uzun süren görevlerin gecikmesini diğer faydalı hesaplamaların arkasına gizlemektir . Bekleme süresini yararlı bir işle doldurabilirseniz, bu zaman kazandırır. Yapamazsan, iyi, operasyonun async olması yüzünden hiçbir şey kaybolmaz

Bu nedenle, dillerinizin size sunduğu asenkron işlemleri kabul etmenizi öneririm. Zaman kazanmak için oradalar.


işlemlerin engellenmediği bir işlevsel dil düşünüyordum, bu yüzden senkronize bir sözdizimine sahip olsa bile, uzun süredir devam eden bir hesap
diziyi

6
@Cinn Ben soruda bulamadım ve sorudaki örnek bu özelliğe sahip olmayan Javascript. Bununla birlikte, genellikle bir derleyicinin tanımladığınız gibi paralelleştirme için anlamlı fırsatlar bulması oldukça zordur: Böyle bir özelliğin anlamlı bir şekilde kullanılması, programcının uzun bir gecikme çağrısından hemen sonra ne koyduğunu açıkça düşünmesini gerektirecektir . Çalışma zamanını programlayıcıda bu gereksinimden kaçınmak için yeterince akıllı yaparsanız, çalışma zamanınız muhtemelen işlev çağrıları arasında agresif bir şekilde paralelleştirilmesi gerekeceğinden performans tasarrufunu azaltacaktır.
cmaster

2
Tüm bilgisayarlar aynı hızda bekler.
Bob Jarvis - Monica

2
@BobJarvis Evet. Fakat bekleme süresinde ne kadar iş yapabilecekleri konusunda farklılık gösterirler ...
cmaster

13

Bazıları yapar.

Ana akım değiller (henüz) çünkü async, yeni bir özellik olsa bile, sadece iyi bir özellik bile olsa ya da programcılara nasıl kolay / kullanışlı bir şekilde sunulabileceğimize dair daha iyi bir fikir edindik. dışavurumcu / vb. Mevcut eşzamansız özellikler, biraz farklı bir tasarım yaklaşımı gerektiren mevcut dillere büyük ölçüde cıvatalanmıştır.

Bu, her yerde yapmak açıkça iyi bir fikir olmadığını söyledi . Sık karşılaşılan bir başarısızlık, döngüsel olarak asenkron çağrı yapmak ve bunların etkin bir şekilde yürütülmesini sağlamaktır. Eşzamansız aramaların kapalı olması bu tür bir hatayı gizleyebilir. Ayrıca, bir Task<T>(veya dilinizin eşdeğeri) 'ne kadar zorla baskı yapılmasını destekliyorsanız T, bu, programlayıcınıza biraz karmaşıklık / maliyet ekleyebilir ve bu iki programcının gerçekte hangisinin istediği belli olmadığı zaman hata raporlaması getirebilir.

Ancak bunlar aşılmaz sorunlar değildir. Eğer bu davranışı desteklemek isteseydiniz, neredeyse kesin olarak yapardınız, ancak takaslar olsa da.


1
Bence bir fikir her şeyi eşzamansız fonksiyonlara sarmak olabilir, eşzamanlı görevler hemen çözülür ve hepimiz halletmemiz gerekir (Düzenleme: @ amon bunun neden kötü bir fikir olduğunu açıkladı ...)
Cinn

8
" Bazıları " için birkaç örnek verebilir misiniz , lütfen?
Bergi

2
Asenkron programlama hiçbir şekilde yeni değildir, sadece günümüzde insanların bununla daha sık uğraşması gerekiyor.
Kübik

1
@Cubic - bildiğim kadarıyla bir dil özelliğidir. Daha önce sadece (garip) userland fonksiyonları oldu.
Telastyn

12

Bunu yapan diller var. Ancak, mevcut dil özellikleri ile kolayca gerçekleştirilebildiğinden, aslında pek bir ihtiyaç yoktur.

Sürece sahip olarak bazı asenkroniye ifade yolunu, sen uygulayabilir Futures veya Promises tamamen bir kütüphane özellik olarak, herhangi bir özel dil özelliklerini gerekmez. Ve sürece sahip olarak bazı ifade Şeffaf Proxy'leri , birlikte iki özelliği koyabilirsiniz ve varsa Şeffaf Futures .

Örneğin, Smalltalk ve torunlarında, bir nesne kimliğini değiştirebilir, kelimenin tam anlamıyla farklı bir nesne olabilir (ve aslında bunu yapan yöntem Object>>become:).

A döndüren uzun süren bir hesaplamayı düşünün Future<Int>. Bu Future<Int>, Intfarklı uygulamalar dışında , aynı yöntemlere sahiptir . Future<Int>bireyin +başka bir numara eklemek ve sonucu dönmez yöntem, yeni bir döndüren Future<Int>hesaplama sarar hangi. Ve böyle devam eder. Makul bir döndürme ile uygulanması mümkün değildir yöntemleri Future<Int>yerine, otomatik olarak olur awaitSonuç, daha sonra çağrı self become: result.olan (şu anda yürütülmesi nesne yapacak self, yani Future<Int>) tam anlamıyla haline resultbir eskiden nesne referans artık, yani bir amacı Future<Int>ise şimdi Inther yerde, müşteriye tamamen şeffaf.

Özel eşzamansız dil özellikleri gerekmez.


Tamam, ama bu ikisi varsa sorunları var Future<T>ve Tbazı ortak bir arayüz paylaşmak ve bunu arayüzden işlevini kullanın. becomeSonuç olmalı ve sonra işlevselliği kullanmalı mı, kullanmamalı mı? Bir eşitlik operatörü veya bir hata ayıklama temsili gibi şeyler düşünüyorum.
öğleden

Herhangi bir özellik eklemediğini anlıyorum, mesele şu ki derhal çözümleme hesaplamalarını ve uzun süren hesaplamaları yazma konusunda farklı sözdizimlerimiz var ve ondan sonra sonuçları başka amaçlar için aynı şekilde kullanırız. Her ikisini de şeffaf bir şekilde ele alan, onu daha okunaklı hale getiren bir sözdizimine sahip olup olmadığımızı merak ediyordum ve böylece programcının onu ele alması gerekmiyordu. Yapıyor gibi a + ba ve b hemen veya sonradan mevcut olup olmadığını, her iki tamsayılar, hiçbir konularda, sadece yazma a + b(yapmak mümkün hale Int + Future<Int>)
Cinn

@Cinn: Evet, Şeffaf Vadeli İşlemlerle bunu yapabilirsiniz ve bunun için özel bir dil özelliğine ihtiyacınız yoktur. Bunu, zaten Smalltalk, Self, Newspeak, Us, Korz, Io, Ioke, Seph, ECMAScript ve görünüşe göre Python'da okuduğum gibi mevcut özellikleri kullanarak uygulayabilirsiniz.
Jörg W Mittag

3
@ amon: Şeffaf Gelecekler fikri, bunun bir gelecek olduğunu bilmediğinizdir. Sizin bakış açınıza göre, aralarında ortak bir arayüz yoktur Future<T>ve Tçünkü sizin bakış açınıza göre sadece a yokturFuture<T>T . Şimdi, elbette, bu işin nasıl verimli bir şekilde yapılacağı, hangi işlemlerin blokaja karşı bloke edilmemesi gerektiği vb. Üzerinde birçok mühendislik zorluğu var, ancak bunu bir dil veya kütüphane özelliği olarak yapıp yapmadığınızdan bağımsızdır. Şeffaflık soruda OP'nin öngördüğü bir gereklilikti, zor olduğunu ve mantıklı olamayacağını iddia etmiyorum.
Jörg W Mittag

3
@ Jörg Bu kodun aslında bu modelde ne zaman yürütüldüğünü bilme şansınız olmadığından, işlevsel dillerden başka hiçbir şeyde sorun çıkarmış gibi görünüyor. Bu genellikle Haskell'de iyi sonuç verir, ancak bunun daha prosedürel dillerde nasıl işleyeceğini göremiyorum (ve Haskell'de bile, performansı önemsiyorsanız, bazen bir yürütmeyi zorlamak ve altta yatan modeli anlamak zorundasınız). Yine de ilginç bir fikir.
Voo

7

Onlar (iyi, çoğu). Aradığınız özellik olarak adlandırılan olduğunu ipler .

Bununla birlikte, konuların kendi problemleri vardır:

  1. Kod askıya alınabilir Çünkü herhangi bir noktada , sen olamaz hiç bu işler "kendileri tarafından" değişmeyecek varsayalım. İpliklerle programlama yaparken, programınızın değişen şeylerle nasıl başa çıkması gerektiğini düşünerek çok zaman harcıyorsunuz.

    Bir oyun sunucusunun, bir oyuncunun başka bir oyuncuya saldırısını gerçekleştirdiğini hayal edin. Bunun gibi bir şey:

    if (playerInMeleeRange(attacker, victim)) {
        const damage = calculateAttackDamage(attacker, victim);
        if (victim.health <= damage) {
    
            // attacker gets whatever the victim was carrying as loot
            const loot = victim.getInventoryItems();
            attacker.addInventoryItems(loot);
            victim.removeInventoryItems(loot);
    
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon} and you die!");
            victim.setDead();
        } else {
            victim.health -= damage;
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon}!");
        }
        attacker.markAsKiller();
    }
    

    Üç ay sonra bir oyuncu attacker.addInventoryItems, koşarken tam olarak öldüğü ve oturumu kapattığında , o victim.removeInventoryItemszaman başarısız olacağını, eşyalarını tutabildiğini ve saldırganın da eşyalarının bir kopyasını alacağını keşfeder . Bunu birkaç kez yapıyor, ince havadan bir milyon ton altın üretiyor ve oyunun ekonomisini düşürüyor.

    Alternatif olarak, oyun kurbana bir mesaj gönderirken saldırgan oturumu kapatabilir ve başının üstünde bir "katil" etiketi bulunmaz, böylece bir sonraki kurban ondan kaçmaz.

  2. Kod herhangi bir noktada askıya alınabildiğinden , veri yapılarını işlerken her yerde kilit kullanmanız gerekir. Bir oyunda açık sonuçları olan yukarıda bir örnek verdim, ancak daha ince olabilir. Bağlantılı bir listenin başına bir öğe eklemeyi düşünün:

    newItem.nextItem = list.firstItem;
    list.firstItem = newItem;
    

    Bu, sorunların yalnızca G / Ç yaptıklarında askıya alınabileceğini ve herhangi bir noktada askıya alınabileceğini söylemezseniz, sorun değildir. Ancak, bir G / Ç işlemi olan bir durumu hayal edebileceğinizden emin olabilirsiniz - günlüğe kaydetme gibi:

    for (player = playerList.firstItem; player != null; player = item.nextPlayer) {
        debugLog("${item.name} is online, they get a gold star");
        // Oops! The player might've logged out while the log message was being written to disk, and now this will throw an exception and the remaining players won't get their gold stars.
        // Or the list might've been rearranged and some players might get two and some players might get none.
        player.addInventoryItem(InventoryItems.GoldStar);
    }
    
  3. Kod herhangi bir noktada askıya alınabildiği için , kaydedilmesi gereken çok fazla durum olabilir. Sistem, bununla, her bir dişine tamamen ayrı bir yığın vererek başa çıkmaktadır. Ancak yığın oldukça büyük, bu yüzden 32 bit programda yaklaşık 2000 konuya sahip olamazsınız. Veya çok küçük yapma riski altında, istif boyutunu küçültebilirsiniz.


3

Buradaki cevapların çoğu yanıltıcıdır, çünkü soru tam anlamıyla eşzamansız programlama hakkında ve engellemeyen IO hakkında sorular sorsa da, bu davada diğerini tartışmadan tartışabileceğimizi sanmıyorum.

Eşzamansız programlama doğası gereği, eşzamanlı olarak eşzamanlı olmasa da, eşzamansız programlamanın doğası çoğunlukla çekirdeğin dişlerini tıkamaktan kaçınmaktadır. Node.js, Promiseengelleme işlemlerinin bir olay döngüsünden gönderilmesine izin vermek için geri çağrılar veya CompletableFutures aracılığıyla eşzamansızlık kullanır ve Java'daki Netty de benzer bir şey yapmak için geri çağrılar ya da s aracılığıyla eşzamansızlık kullanır .

Bununla birlikte, bloke edici olmayan kod, zaman uyumsuzluk gerektirmez . Programlama dilinizin ve çalışma zamanınızın sizin için ne kadar istekli olduğuna bağlıdır.

Git Erlang ve Haskell / GHC bunu sizin için halledebilir. Gibi bir şey yazabilir var response = http.get('example.com/test')ve yanıt beklerken sahnelerin arkasında bir çekirdek ipi bırakmasını sağlayabilirsiniz . Bu, goroutinler, Erlang işlemleri veya forkIOengelleme sırasında perdenin arkasındaki çekirdek ipliklerini bırakarak bir cevap beklerken başka şeyler yapmasına izin vererek yapılır.

Dilin sizin için asenkronizasyonu gerçekten idare edemediği doğrudur, ancak bazı soyutlamalar diğerlerinden daha ileri gitmenize izin verir; örneğin, sınırsız devam edenler veya asimetrik koroinler. Ancak, asenkron kod birincil nedeni, sistem çağrıları engelleme, kesinlikle olabilir geliştirici uzak soyutlanabilir.

Node.js ve Java, eşzamanlı olmayan engelleyici olmayan kodu desteklerken, Go ve Erlang eşzamanlı olmayan engelleyici olmayan kodu destekler. İkisi de farklı değişimler içeren geçerli yaklaşımlar.

Benim oldukça öznel bir argümanım, geliştirici adına engellenmemeyi yöneten çalışma sürelerine karşı çıkanların, ilk geceleri çöp toplanmasına karşı çıkanlara benziyor olmasıdır. Evet, bir maliyete sahiptir (bu durumda öncelikle daha fazla bellek), ancak geliştirme ve hata ayıklamayı kolaylaştırır ve kod tabanlarını daha sağlam hale getirir.

Şahsen, asenkronize olmayan engelleyici olmayan kodun gelecekteki programlama sistemleri için ayrılması gerektiğini ve daha modern teknoloji yığınlarının uygulama geliştirme için senkronize edici engelsiz olmayan çalışma sürelerine geçmesi gerektiğini savunuyorum .


1
Bu gerçekten ilginç bir cevaptı! Ancak “senkronize” ve “asenkron” olmayan kodlama arasındaki farkınızı anladığımdan emin değilim. Benim için, senkronize engellemeyen kod, engellemek zorunda waitpid(..., WNOHANG)kalırsa bunun gibi bir C işlevi gibi bir şeyin başarısız olduğu anlamına gelir . Yoksa burada “senkronize”, “programcı tarafından görülebilen geri aramalar / durum makineleri / olay döngüleri” anlamına mı geliyor? Fakat Go örneğiniz için, bir kanaldan okuyarak bir goroutinin sonucunu açıkça beklemeliyim, değil mi? Bu nasıl JS / C # / Python'da eşzamansız / beklemeden daha az eşzamansız?
amon

1
Geliştiriciye maruz kalan programlama modelini tartışmak için "asenkron" ve "senkron" kullanıyorum ve bu sırada yararlı bir şey yapamayacağı bir çekirdek iş parçacığının engellenmesini tartışmak için "engelleme" ve "engelleme" özelliğini kullanıyorum Yapması gereken diğer hesaplamalar ve kullanabileceği yedek bir mantıksal işlemci var. Eh, bir gorout, altta yatan ipliği engellemeden sadece bir sonuç için bekleyebilir, ancak başka bir goroin eğer isterse bir kanal üzerinden iletişim kurabilir. Goroutinin engelleyici olmayan bir soketin okumasını beklemek için doğrudan bir kanal kullanması gerekmez .
Louis Jackman,

Hmm, şimdi farkınızı şimdi anlıyorum. Korotinler arasındaki veri ve kontrol akışını yönetme konusunda daha fazla endişelenmeme rağmen, ana çekirdek ipliğini asla engellememe konusunda endişelisiniz. Go veya Haskell'in bu konuda C ++ veya Java'ya göre avantajları olduğundan emin değilim, çünkü bunlar da arka plandaki konuları açabilir, bu yüzden sadece biraz daha fazla kod gerektirir.
amon,

@LouisJackman, sistem programlaması için engelleme yapmayan zaman uyumsuzluğuyla ilgili son ifadenizi biraz detaylandırabilir. Asenkron bloke edici olmayan yaklaşımın avantajları nelerdir?
sunprophit

@sunprophit Asenkron engellemesiz sadece bir derleyici dönüşümüdür (genellikle asenkron / beklemede), senkronize engellemesiz işlem, bazı karmaşık yığın manipülasyonlarının bir kombinasyonu gibi çalışma zamanı desteği gerektirir, fonksiyon çağrıları üzerine verim noktaları ekler (satır içi ile çarpışabilen). azaltmalar ”(BEAM gibi bir VM gerektirir), vb. Çöp toplama gibi, kullanım kolaylığı ve sağlamlık için daha az çalışma zamanı karmaşıklığıyla yatırım yapar. C, C ++ ve Rust gibi sistem dilleri, hedeflenen etki alanları nedeniyle bunun gibi daha büyük çalışma zamanı özelliklerinden kaçınırlar;
Louis Jackman 11

2

Sizi doğru okuyorsam, senkronize bir programlama modeli, ancak yüksek performanslı bir uygulama istiyorsunuz. Eğer bu doğruysa, bu bize zaten yeşil iplik şeklinde veya örneğin Erlang veya Haskell'in işlemlerinde mevcuttur. Bu yüzden evet, bu harika bir fikir, ancak mevcut dillere uyarlama yapmak istediğiniz kadar yumuşak olamaz.


2

Soruyu takdir ediyorum ve cevapların çoğunu yalnızca statükoyu savunmak için buluyorum. Düşük ila yüksek seviyeli diller yelpazesinde, bir süredir bir sıkıntı içinde sıkışıp kaldık. Bir sonraki yüksek seviye açıkça sözdizimine (daha önce bekleyen ve zaman uyumsuz anahtar kelimeler gibi açık anahtar kelimelere olan ihtiyaç) daha az odaklanan ve niyet hakkında çok daha fazla bir dil olacak. (Charles Simonyi'ye açık kredi, ancak 2019 ve geleceği düşünüyorum.)

Bir programcıya söylersem, basitçe bir veritabanından bir değer alan bir kod yazın, güvenli bir şekilde, "ve BTW, Kullanıcı Arayüzünü asmayın" ve "hataları bulmakta zorlanan başka düşünceler getirmeyin" diyebilirsiniz. ". Geleceğin programcıları, yeni nesil dil ve araçlarla, kesinlikle tek bir kod satırında bir değer getiren ve oradan geçen bir kod yazabilecekler.

En üst seviye dil İngilizce konuşmak ve gerçekten ne yapmak istediğinizi bilmek görev yapan kişinin yetkinliğine güvenmek olacaktır. (Bilgisayarı Star Trek'te düşünün ya da Alexa'dan bir şey isteyin.) Bundan çok uzaktayız, ancak daha yakınımda ve beklentim, dilin / derleyicinin o ana kadar ileri gitmeden sağlam, amaçlı bir kod oluşturmak için daha fazla olabileceği yönünde. AI'ya ihtiyacım var.

Bir yandan, bunu yapan ve tüm sözdizimsel tekniklere bağlı olmayan, Scratch gibi daha yeni görsel diller var. Elbette, programcıların endişelenmesine gerek kalmayacak birçok sahne çalışması var. Bu, Scratch'ta işletme sınıfı yazılımı yazmıyorum, bu yüzden, sizin gibi, olgun programlama dillerinin senkron / asenkron problemi otomatik olarak yönetme zamanının geldiğine dair aynı beklentim var.


1

Tarif ettiğiniz sorun iki katlıdır.

  • Yazdığınız program dışarıdan bakıldığında bir bütün olarak asenkron davranmalıdır .
  • O gerektiğini değil bir işlev çağrısı potansiyel kontrolünü vazgeçer olsun veya olmasın çağrı yerinde görünür.

Bunu başarmanın birkaç yolu var, ama temelde

  1. çoklu dişlilere sahip (bir miktar soyutlama seviyesinde)
  2. Dil seviyesinde çoklu fonksiyon türlerine sahip, hepsine bu denir foo(4, 7, bar, quux).

(1) için birden fazla işlemi birlikte yapmak ve çalıştırmak, birden fazla çekirdek ipliği üretmek ve dil-zamanı düzeyinde iş parçacıklarını çekirdek iş parçacıklarına zamanlayan yeşil iş parçacığı uygulamalarını bir araya getiriyorum. Sorunun perspektifinden bakıldığında, onlar aynı. Bu dünyada hiçbir işlev, ipliğin perspektifinden kontrolü hiç bırakmaz veya kaybetmez . İş parçacığının kendisi bazen denetime sahip değildir ve bazen çalışmamaktadır ancak bu dünyadaki kendi iş parçanızın kontrolünden vazgeçmezsiniz. Bu modele uyan bir sistem yeni dişler açma ya da mevcut dişlilerle birleştirme yeteneğine sahip olabilir ya da olmayabilir. Bu modele uyan bir sistem, Unix'inki gibi bir ipliğin çoğaltılması yeteneğine sahip olabilir veya olmayabilir fork.

(2) ilginç. Adaleti yerine getirmek için giriş ve eleme formları hakkında konuşmalıyız.

Neden örtüklüğün awaitJavascript gibi bir dile geriye dönük olarak uyumlu bir şekilde eklenemediğini göstereceğim. Temel fikir, kullanıcıya verilen sözleri ortaya koyarak ve senkronize ve eşzamansız bağlamlar arasında bir ayrım yapmasıyla Javascript, eşzamanlı ve eşzamansız işlevleri düzgün bir şekilde işlemeyi önleyen bir uygulama detayını sızdırmıştır. Ayrıca await, bir async işlev gövdesinin dışında söz veremeyeceğiniz gerçeği de var . Bu tasarım seçenekleri, "arayanlar için görünmezliği sağlama" ile uyumsuzdur.

Bir lambda kullanarak senkronize bir fonksiyon tanıtabilir ve fonksiyon çağrısıyla ortadan kaldırabilirsiniz.

Senkron fonksiyonu giriş:

((x) => {return x + x;})

Senkron işlev eleme:

f(4)

((x) => {return x + x;})(4)

Bunu eşzamansız fonksiyon tanıtımı ve ortadan kaldırılmasıyla karşılaştırabilirsiniz.

Asenkron fonksiyon tanıtımı

(async (x) => {return x + x;})

Zaman uyumsuz işlev ortadan kaldırması (not: yalnızca bir asyncişlev içinde geçerlidir )

await (async (x) => {return x + x;})(4)

Buradaki temel problem, asenkronize bir fonksiyonun aynı zamanda vaat edilen bir nesneyi üreten senkronize bir fonksiyon olmasıdır .

İşte node.js repl dosyasında eşzamanlı olmayan bir eşzamansız işlev çağırma örneği.

> (async (x) => {return x + x;})(4)
Promise { 8 }

Varsayılan olarak, asenkron ve senkronize işlev çağrıları arasındaki farkın çağrı sitesinde görünmediği ve muhtemelen tanım sitesinde görünmediği, dinamik olarak yazılmış bir dil bile olabilirsiniz.

Böyle bir dili almak ve onu Javascript'e indirmek mümkün, sadece tüm işlevleri etkili bir şekilde eşzamansız yapmak zorundasınız.


1

Go dili goroutines ve Go dili çalışma zamanı, tüm kodu synchrone sanki yazabilirsiniz. Bir işlem bir goroutinde engellerse, diğer goroutinlerdeki işlem devam eder. Kanallarla goroutinler arasında kolayca iletişim kurabilirsiniz. Bu genellikle Javascript veya async / await gibi diğer dillerdeki geri aramalardan daha kolaydır. Bazı örnekler ve açıklamalar için https://tour.golang.org/concurrency/1 adresine bakınız .

Dahası, onunla hiçbir kişisel deneyimim yok, ancak Erlang'ın da benzer tesisleri olduğunu duydum.

Yani evet, senkronize / asenkron problemi çözen Go and Erlang gibi programlama dilleri var, fakat ne yazık ki henüz pek popüler değiller. Bu dillerin popülerliği arttıkça, belki de sağladıkları imkanlar diğer dillerde de uygulanacaktır.


Neredeyse Go dilini hiç kullanmamıştım, ama açıkça ilan etmiş gibi görünüyorsun go ..., yani await ...hayır gibi görünüyor ?
Cinn

1
@Cinn Aslında hayır. Herhangi bir aramayı goroutine olarak kendi lifi / yeşil ipliğine ekleyebilirsiniz go. Ve hemen hemen engelleyebilecek herhangi bir çağrı, bu arada (eşzamanlı çoklu görevlendirme) sadece farklı bir goroine geçiş yapan çalışma zamanı tarafından eşzamansız olarak yapılır. Bir mesaj bekleyerek bekliyorsun.
Deduplicator

2
Goroutinler bir tür eşzamanlılık olsa da, onları eşzamansız / beklediğinizle aynı kepçeye koymuyorum: kooperatif koroutinler değil, otomatik olarak (ve önleyici olarak!) Zamanlanmış yeşil iplikler. Fakat bu da otomatik olarak beklemeyi beklemiyor: Go'nun karşılığı awaitbir kanaldan okumak <- ch.
amon

@ amon Bildiğim kadarıyla, goroutinler yerel iş parçacıkları üzerinde (normalde gerçek donanım paralelliğini maksimuma çıkarmak için yeterli) ortak çalışma zamanlamasına göre programlanıyorlar ve bunlar işletim sistemi tarafından önceden planlanıyor.
Deduplicator

OP, "senkronize olmayan bir şekilde asenkron kod yazabilmeyi" istedi. Bahsettiğiniz gibi, goroutines ve go runtime ile tam olarak bunu yapabilirsiniz. Iş parçacığı ayrıntıları hakkında endişelenmenize gerek yok, sadece kod okuma ve yazma, kod senkronize sanki yazma ve varsa diğer goroutinleri çalışmaya devam edecek Ayrıca, bu avantajı elde etmek için "beklemek" veya bir kanaldan okumak zorunda bile değilsiniz. Bu nedenle Go'nun OP'nin arzularını en iyi şekilde karşılayan bir programlama dili olduğunu düşünüyorum.

1

Henüz yükseltilmemiş çok önemli bir yönü var: reentrancy. Zaman uyumsuz arama sırasında çalışan başka bir kodunuz varsa (yani: olay döngüsü) (ve neden yoksa zaman zaman uyumsuzluk gerektiriyor mu?), O zaman kod program durumunu etkileyebilir. Arayan kişiden gelen eşzamansız çağrıları gizleyemezsiniz, çünkü çağrı yapan kişi, işlev durumunun süresi boyunca etkilenmeden kalması için program durumunun bazı bölümlerine bağlı olabilir. Örnek:

function foo( obj ) {
    obj.x = 2;
    bar();
    log( "obj.x equals 2: " + obj.x );
}

Eğer bar()bir zaman uyumsuz fonksiyonudur için o zaman mümkün olabilir obj.x's yürütülürken değiştirmek için. Bu, çubuğun zaman uyumsuz olduğuna ve bu etkinin mümkün olduğuna dair herhangi bir ipucu olmadan beklenmeyen bir durum olacaktır. Tek alternatif, olası her işlev / yöntemin eşzamansız olduğundan şüphelenmek ve her işlev çağrısından sonra yerel olmayan durumları yeniden almak ve yeniden kontrol etmek olacaktır. Bu, ince hatalara eğilimlidir ve yerel olmayan durumun bir kısmı işlevler yoluyla alındığında bile mümkün olmayabilir. Bu nedenle, programcının beklenmedik şekillerde hangi fonksiyonların program durumunu değiştirme potansiyeline sahip olduğunun farkında olması gerekir:

async function foo( obj ) {
    obj.x = 2;
    await bar();
    log( "obj.x equals 2: " + obj.x );
}

Şimdi bar()bir async işlevi olduğu açıkça görülüyor ve bunun üstesinden gelmek için doğru yol, obj.xdaha sonra beklenen değeri tekrar kontrol etmek ve olabilecek herhangi bir değişiklikle başa çıkmak.

Diğer cevaplarda daha önce de belirtildiği gibi, Haskell gibi saf işlevsel diller, ortak / küresel bir duruma olan gereksinimden kaçınılarak bu etkiden tamamen kurtulabilir. İşlevsel diller konusunda fazla tecrübem yok, bu yüzden muhtemelen buna karşı yanlıyım, ancak daha büyük uygulamalar yazarken küresel devlet eksikliğinin bir avantaj olduğunu düşünmüyorum.


0

Sorunuzda kullandığınız Javascript durumunda, dikkat edilmesi gereken önemli bir nokta vardır: Javascript tek iş parçacıklıdır ve yürütme sırası eşzamansız çağrı olmadığı sürece garanti edilir.

Yani sizinki gibi bir diziniz varsa:

const nbOfUsers = getNbOfUsers();

Bu arada başka hiçbir şeyin yürütülmeyeceği garanti edilir. Kilitler veya benzeri bir şey için gerek yok.

Ancak, getNbOfUserszaman uyumsuzsa, o zaman:

const nbOfUsers = await getNbOfUsers();

Bu, getNbOfUsersçalıştırırken çalıştırma verimi ve diğer kodların arasında çalıştırılabileceği anlamına gelir . Bu, ne yaptığınıza bağlı olarak, bazı kilitlemelerin gerçekleşmesini gerektirebilir.

Bu nedenle, bir çağrının eşzamansız olduğu ve olmadığında farkında olmak iyi bir fikirdir, bazı durumlarda çağrıların eşzamanlı olması durumunda gerekmeyecek ek önlemler almanız gerekir.


Haklısın, sorudaki ikinci kodum getNbOfUsers()bir Söz vermiş gibi geçersiz . Ancak bu tam olarak sorumumun amacı, neden açıkça zaman uyumsuz olarak yazmamız gerekiyor, derleyici bunu algılayabilir ve farklı bir şekilde otomatik olarak ele alabilir.
Cinn

@Cinn bu benim amacım değil. Demek istediğim, eşzamanlı olmayan bir çağrı yürütülürken yürütme akışının kodunuzun diğer bölümlerine ulaşabileceği, eşzamanlı çağrı için mümkün olmadığıdır. Çalışan birden çok iş parçacığı olması gibi ama bunun farkında değil gibi olur. Bu büyük sorunlara yol açabilir (genellikle tespit edilmesi ve çoğaltılması zor olan).
Jcaron

-4

Bu, std::asyncC ++ 11'den beri C ++ ile kullanılabilir.

Template işlevi async, f işlevini asenkron olarak çalıştırır (potansiyel olarak bir iş parçacığı havuzunun parçası olabilecek ayrı bir iş parçacığında) ve sonunda bu işlev çağrısının sonucunu tutacak bir std :: future döndürür.

Ve C ++ 20 ile birlikte koroinler kullanılabilir:


5
Bu soruya cevap gibi görünmüyor. Bağlantınıza göre: "Coroutines TS bize ne veriyor? Üç yeni dilde anahtar kelimeler: co_await, co_yield ve co_return" ... Ama asıl soru, neden bir await(veya co_awaitbu durumda) anahtar kelimeye ihtiyacımız var ?
Arturo Torres Sánchez
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.