Neden zaman uyumsuz anahtar kelimeye ihtiyacımız var?


19

Net 4.5'te async / await ile oynamaya başladım. Başlangıçta merak ettiğim bir şey var, async anahtar sözcüğü neden gerekli? Okuduğum açıklama, bir işaretleyici olmasıydı, böylece derleyici bir yöntemin bir şey beklediğini biliyor. Ama derleyici bir anahtar kelime olmadan bunu anlamak gerekir gibi görünüyor. Peki başka ne yapıyor?


2
İşte C # 'daki anahtar kelimeyi ve F #' daki ilgili işlevselliği açıklayan bazı ayrıntılara giren gerçekten iyi bir blog makalesi (dizi).
paul

Bence bu derleyici için değil, geliştirici için bir belirteç.
CodesInChaos


@svick teşekkürler, aradığım şey bu. Şimdi mükemmel bir mantıklı.
ConditionRacer

Yanıtlar:


23

Burada birkaç cevap var ve hepsi asenkron yöntemlerin ne yaptığı hakkında konuşmuyor, ancak hiçbiri soruyu cevaplamıyor, bu yüzden asyncişlev bildiriminde yer alan bir anahtar kelime olarak gerekli.

"Derleyiciyi işlevi özel bir şekilde dönüştürmesi için yönlendirmek" değildir; awaityalnız bunu yapabilirdi. Neden? C # zaten yöntem gövdesinin özel bir anahtar kelime bulunması aşırı (ve çok benzer gerçekleştirmek derleyici neden olan başka bir mekanizma olduğundan async/awaityöntem vücuda) dönüşümler: yield.

Bunun dışında yieldC # 'da kendi anahtar kelimesi değil ve neden açıklayacağını anlamak async. Bu mekanizmayı destekleyen çoğu dilde aksine, C # 'de bunun yerine yield value;söylemek zorunda olduğunuzu söyleyemezsiniz yield return value;. Neden? Çünkü C # zaten var olduktan sonra dile eklenmişti ve birisinin, bir yerlerde yieldbir değişkenin adı olarak kullanılmış olabileceğini varsaymak oldukça mantıklıydı . Ancak <variable name> return, sözdizimsel olarak doğru olan önceden var olan bir senaryo olmadığından yield return, mevcut kodla% 100 geriye dönük uyumluluğu korurken jeneratörleri tanıtmayı mümkün kılmak için dile eklenmiştir.

Bu yüzden asyncbir işlev değiştirici olarak eklendi: awaitdeğişken adı olarak kullanılan mevcut kodu kırmamak için . Hiçbir asyncyöntem mevcut olmadığından, eski kod geçersiz kılınmaz ve yeni kodda, derleyici, tanımlayıcı olarak değil anahtar sözcük olarak ele alınması gerektiğini asyncbilmek için etiketin varlığını kullanabilir await.


3
Bu makalenin de söylediği şey bu (aslında yorumlarda @svick tarafından gönderildi), ancak kimse bunu bir cevap olarak yayınlamadı. Sadece iki buçuk yıl sürdü! Teşekkürler :)
ConditionRacer

Bu, gelecekteki bazı sürümlerde, asyncanahtar kelimeyi belirtme gereksiniminin bırakılabileceği anlamına gelmez mi? Açıkçası bu bir BC molası olurdu, ancak çok az projeyi etkilediği ve yükseltmek için basit bir gereklilik olduğu düşünülebilir (yani bir değişken adını değiştirmek).
Quolonel Soruları

6

yöntemi normal bir yöntemden, kod üretimi için tamamen farklı bir yaklaşım gerektiren geri arama özellikli bir nesneye değiştirir

ve böyle sert bir şey olduğunda bunu açıkça belirtmek gelenekseldir (bu dersi C ++ 'dan öğrendik)


Yani bu gerçekten okuyucu / programcı için bir şey ve derleyici için çok fazla değil mi?
Durum Değiştirici

@ Justin984 kesinlikle derleyiciye özel bir şey olması için sinyal yardımcı olur
cırcır ucube

Sanırım derleyicinin neden buna ihtiyacı olduğunu merak ediyorum. Sadece yöntemi ayrıştırarak beklemenin yöntem gövdesinde bir yerde olduğunu göremez miydi?
Durum Değiştirici

asyncaynı anda birden fazla s zamanlamak istediğinizde yardımcı olur, böylece birbiri ardına değil aynı anda çalışırlar (en azından iş parçacıkları mevcutsa)
mandal ucube

@ Justin984: Geri dönen her yöntem Task<T>aslında bir asyncyöntemin özelliklerine sahip değildir - örneğin, sadece bazı iş / fabrika mantığından geçebilir ve daha sonra başka bir yöntem döndürmeye yetki verebilirsiniz Task<T>. asyncyöntemler bir dönüş türü Task<T>de imza yok ama aslında bir dönüş Task<T>, sadece bir dönüş T. Tüm bunları asyncanahtar kelime olmadan çözmek için, C # derleyicisi yöntemin her türlü derin denetimini yapmak zorunda kalacaktı, bu da muhtemelen biraz yavaşlatacak ve her türlü belirsizliğe yol açacaktı.
Şubat'ta Aaronaught

4

"Eşzamansız" veya "güvensiz" gibi anahtar kelimelerle ilgili tüm fikir, değiştirdikleri kodun nasıl ele alınacağına ilişkin belirsizliği ortadan kaldırmaktır. Zaman uyumsuz anahtar kelimesinde derleyiciye değiştirilen yöntemi hemen geri dönmesi gerekmeyen bir şey olarak ele almasını söyler. Bu, bu yöntemin kullanıldığı iş parçacığının, o yöntemin sonuçlarını beklemek zorunda kalmadan devam etmesini sağlar. Etkili bir kod optimizasyonu.


İlk paragraf doğru olsa da, kesintilerle karşılaştırma yanlıştır (bilgili düşünceme göre).
paul

Onları amaç açısından biraz benzer olarak görüyorum, ancak açıklık uğruna kaldıracağım.
Dünya Mühendisi

Kesmeler aklımdaki zaman uyumsuz koddan çok olaylara benziyor - normal kod devam ettirilmeden önce bir bağlam anahtarına ve tamamlanmasına neden oluyorlar (ve normal kod da çalışmasından tamamen habersiz olabilir), ancak zaman uyumsuz kodla yöntem çağıran evre devam ettirilmeden önce tamamlanmamıştır. Kaputun altında farklı uygulamalar gerektiren farklı mekanizmalar söylemeye çalıştığınızı görüyorum, ancak kesintiler bu noktayı göstermek için benimle dalga geçmedi. (Kalanlar için +1, btw.)
paul

0

Tamam, işte benim almam.

On yıllardır bilinen koroutin adı verilen bir şey var . ("Knuth and Hopper" -class "onlarca yıldır") Bunlar alt işlevlerin sadece işlev başlatma ve geri dönüş ifadesinde kontrolü alıp bırakmazlar, aynı zamanda belirli noktalarda da ( süspansiyon noktaları ) yaparlar . Bir alt rutin, süspansiyon noktası olmayan bir koroutindir.

Aşağıdaki makalede "protothreads" ile ilgili olarak gösterildiği gibi, C makrolarıyla uygulanması kolay DÜZ KOLAYDIR. ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Okuyun. Bekleyeceğim...

Bunun sonucu, makroların büyük bir switchcase her bir askı noktasında ve bir etiket . Her askıya alma noktasında, işlev hemen takip eden caseetiketin değerini saklar , böylece bir dahaki sefere yürütme işlemine nerede devam edileceğini bilir. Ve kontrolü arayan kişiye geri döndürür.

Bu, "protothread" de açıklanan kodun görünür kontrol akışını değiştirmeden yapılır.

Şimdi tüm bu "prototipleri" çağıran büyük bir döngüye sahip olduğunuzu ve aynı anda tek bir iş parçacığında "protothreads" yürüttüğünüzü hayal edin.

Bu yaklaşımın iki dezavantajı vardır:

  1. Yeniden başlatmalar arasındaki durumu yerel değişkenlerde tutamazsınız.
  2. "Protothread" i rastgele bir arama derinliğinden askıya alamazsınız. (tüm askı noktaları 0 seviyesinde olmalıdır)

Her ikisi için de geçici çözümler vardır:

  1. Tüm yerel değişkenler prototipin içeriğine (protothread'in bir sonraki devam etme noktasını saklaması gerektiği için zaten gerekli olan bağlam) alınmalıdır.
  2. Gerçekten bir prototipten başka bir prototip çağırmanız gerektiğini düşünüyorsanız, bir çocuk prototipi "doğurun" ve çocuğun tamamlanmasına kadar askıya alın.

Ve makroların ve geçici çözümün yaptığı yeniden yazma işini yapmak için derleyici desteğine sahipseniz, protothread kodunuzu yalnızca bir anahtar kelimeyle askıya alma noktaları istediğiniz gibi yazıp yazabilirsiniz.

Ve işte bu asyncve awaitbununla ilgili: (yığınsız) couteinler yaratmak.

C # 'daki eşbiçimler (jenerik veya jenerik olmayan) sınıfının nesneleri olarak yeniden tanımlanır Task.

Bu anahtar kelimeleri çok yanıltıcı buluyorum. Zihinsel okumam:

  • async "askıya alınabilir" olarak
  • await "tamamlanıncaya kadar askıya al" olarak
  • Task "gelecek ..." olarak

Şimdi. İşlevi gerçekten işaretlememiz gerekiyor asyncmu? İşlevi bir program yapmak için kod yeniden yazma mekanizmalarını tetiklemesi gerektiğini söylemenin yanı sıra, bazı belirsizlikleri de giderir. Bu kodu düşünün.

public Task<object> AmIACoroutine() {
    var tcs = new TaskCompletionSource<object>();
    return tcs.Task;
}

asyncZorunlu olmadığını varsayarsak , bu bir koroutin veya normal bir işlev midir? Derleyici bunu bir program olarak yeniden yazmalı mı, yazmamalı mı? Her ikisi de farklı nihai anlambilim ile mümkün olabilir.

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.