Neden hiç bir yöntemi 'beklemiyorsun' ve sonra hemen geri dönüş değerini sorguluyorsun?


24

Olarak bu MSDN maddesi , aşağıdaki örnek kodu (biraz kısa tutmak için düzenlenmiş) sağlanmıştır:

public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Department department = await db.Departments.FindAsync(id);

    if (department == null)
    {
        return HttpNotFound();
    }

    return View(department);
}

FindAsyncYöntem, bir alır Departmentkendi kimliği tarafından nesne ve bir döner Task<Department>. Daha sonra null olup olmadığını görmek için departman hemen kontrol edilir. Anladığım kadarıyla, bu şekilde Görev'in değerini sormak , beklenen yöntemden elde edilen değer geri gelene kadar kod yürütülmesini engeller ve bu da senkronize bir çağrıdır.

Bunu neden yaptın? Find(id)Yine de hemen engelleyecekseniz , senkronize yöntemi çağırmanız daha kolay olmaz mıydı?


Uygulama ile ilgili olabilir. ... else return null;Öyleyse, yöntemin gerçekte istediğiniz bölümü bulduğunu kontrol etmeniz gerekir.
Jeremy Kato

Bir asp.net'te hiçbir şey görmüyorum, ancak destop uygulamasında, bu şekilde yaparak kullanıcı
Rémi

İşte tasarımcıdan bekleyen konsepti açıklayan bir bağlantı ... msdn.microsoft.com/en-us/magazine/hh456401.aspx
Jon Raynor

ASP.NET ile ilgili, yalnızca konu kontağı anahtarları sizi yavaşlatıyorsa veya bellek kullanımı formunda birçok iş parçacığı yığınını oluşturuyorsa, sizin için bir sorundur.
Ian

Yanıtlar:


24

Anladığım kadarıyla, bu şekilde Görev'in değerini sormak, beklenen yöntemden elde edilen değer geri gelene kadar kod yürütülmesini engeller ve bu da senkronize bir çağrıdır.

Tam değil.

Aradığınızda await db.Departments.FindAsync(id)görev gönderilir ve geçerli iş parçacığı diğer işlemler tarafından kullanılmak üzere havuza döndürülür. Yürütme akışı engelleniyor (her departmentşeyi doğru anlarsam hemen kullanmamdan bağımsız olarak ), ancak işlemin makinenin bitmesini beklerken diğer işlerde kullanım için serbesttir (ve bir olay veya tamamlama portu ile bildirilir).

Eğer d.Departments.Find(id)çağırdıysanız, iş parçacığı orada oturur ve işlemin çoğu DB üzerinde yapılsa bile yanıtı bekler.

Disk bağlandığında CPU kaynaklarını etkin bir şekilde boşaltırsınız.


2
Ancak, hepsinin awaityaptığını, yöntemin geri kalanının aynı iş parçacığına devam ettiğini işaret ettim (bazı istisnalar vardır; bazı eşzamansız yöntemler kendi iş parçacığını döndürür) ya asyncda aynı iş parçacığına devam etmeyi kabul et yürütmek için kalan kod (gördüğünüz gibi, nasıl asyncçalıştığı konusunda net değilim ). Tarif ettiğiniz şey, daha sofistike bir şekle benziyorThread.Sleep(untilReturnValueAvailable)
Robert Harvey,

1
@RobertHarvey - bunu bir devamı olarak atadı, ancak görevi (ve devamını) bir kez işlenecek şekilde gönderdikten sonra, çalıştırılacak hiçbir şey kalmadı. Belirtmediğiniz sürece aynı iş parçacığında olması garanti edilmez ( ConfigureAwaitiirc aracılığıyla ).
Telastyn

1
Cevabımı gör ... devam, varsayılan olarak orijinal konuya geri döndürüldü.
Michael Brown

2
Sanırım burada neyi özlüyorum. Bunun herhangi bir fayda sağlayabilmesi için ASP.NET MVC çerçevesinin awaitçağrılması gerekir public async Task<ActionResult> Details(int? id). Aksi takdirde, orijinal çağrı çözülmeyi bekleyen sadece engeller department == null.
Robert Harvey,

2
@RobertHarvey await ..."Geri döndüğünde" FindAsyncçağrı bitti. Bekleyen budur. Buna bekleme denir, çünkü kodunuzu işler için bekletir. (Ancak, mevcut iş parçacığını işler için bekletmekle aynı değildir)
user253751 23

17

Gerçekten hiçbir örnek, görevi beklemeden önce birkaç satır beklemenin mümkün olmadığını göstermemesinden nefret ediyorum. Bunu düşün.

Foo foo = await getFoo();
Bar bar = await getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(foo, bar);

Bu, örneklerin teşvik ettiği kod türüdür ve haklısınız. Bunun çok az bir anlamı var. UI girişine cevap verme gibi başka şeyler yapmak için ana iş parçacığını serbest bırakır, ancak asenkron / beklemenin asıl gücü, potansiyel olarak uzun süredir devam eden bir işin tamamlanmasını beklerken kolayca başka şeyler yapmaya devam edebilmemdir. Yukarıdaki kod “bloke edecek” ve Foo & Bar'ı alana kadar baskı satırını yürütmeyi bekleyecektir. Yine de beklemeye gerek yok. Beklerken bunu işleyebiliriz.

Task<Foo> foo = getFoo();
Task<Bar> bar = getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(await foo, await bar);

Şimdi, yeniden yazılmış kodla, durmamız gerekmiyor ve değerlerimizi beklememiz gerekmiyor. Her zaman bu tür fırsatları araştırıyorum. Ne zaman beklediğimiz konusunda akıllı olmak önemli performans iyileştirmelerine yol açabilir. Bugünlerde birçok çekirdeğimiz var.


1
Hm, bu çekirdek / iş parçacığı hakkında daha az ve asenkron çağrıların doğru kullanılması hakkında daha fazla
Deduplicator

Yanlış değilsin @Dedlicator. Bu yüzden cevabımda “blok” yazdım. İpliklerden bahsetmeden bu şeylerle konuşmak zordur, ancak yine de kabul ederken çok iş parçacığı olabileceğini veya katılmayabileceğini kabul edebilirsiniz.
RubberDuck

Başka bir yöntem çağrısının içinde beklerken dikkatli olmanızı öneririm .. Bazen derleyici bu beklemeyi yanlış anlıyor ve çok garip bir istisna elde ediyorsunuz. Tüm eşzamansız / beklemede sadece sözdizimi şeker, sonra oluşturulan kodun nasıl göründüğünü sharplab.io ile kontrol edebilirsiniz. Bunu birkaç kez gözlemledim ve şimdi sonucun gerekli olduğu görüşmenin üzerinde bir satır bekledim ... bu baş ağrısına gerek yok.
Mart’ta

“Bunda küçük bir anlam var.” - Bunda bir anlam var. "Başka şeyler yapmaya devam et" in aynı yöntemde geçerli olduğu durumlar yaygın değildir; Sadece awaitipliğin yerine tamamen farklı şeyler yapmasını istemek ve izin vermek çok daha yaygındır .
Sebastian Redl 6

“Bu konuda çok az bir anlam var” dediğimde @SebastianRedl, her iki görevi de çalıştırmak yerine, bekle, koş, bekle yerine paralel olarak çalıştırabileceğiniz anlamına gelmişti. Bu düşündüğünden çok daha yaygın. İddiaya girerim kod üssünün etrafına bakarsan, fırsat bulabilirsin.
RubberDuck

6

Buradaki sahnelerin arkasında olan daha çok şey var. Async / Await, sözdizimsel şekerdir. İlk önce FindAsync işlevinin imzasına bakın. Bir Görev döndürür. Zaten anahtar kelimenin büyüsünü görüyorsunuz, bu Görevin bir Departman içinde kutusundan çıkar.

Çağıran fonksiyon engellenmez. Olan, departmana ve beklemede olan anahtar kelimeyi izleyen her şeyin bir kapanmaya kapatılması ve tüm amaç ve amaçlar için Task.ContinueWith yöntemine (FindAsync işlevi otomatik olarak farklı bir iş parçacığında yürütülür) iletilmesidir.

Elbette, sahne arkasına geçen daha çok şey var çünkü işlem orijinal iş parçacığına geri döndürülüyor (böylece bir arkaplan işlemi gerçekleştirirken UI ile senkronize etme konusunda endişelenmenize gerek kalmaz) ve çağıran fonksiyonun Async olması durumunda ( ve eşzamansız olarak adlandırılır) aynı şey yığında olur.

Öyleyse, tuzaklar olmadan, Async operasyonlarının sihrini elde edersiniz.


1

Hayır hemen geri dönmüyor. Bekleyen, yöntem çağrısını asenkronize eder. FindAsync çağrıldığında, Details yöntemi bitmemiş bir görevle döner. FindAsync bittiğinde, sonucunu departman değişkenine döndürecek ve Detaylar yönteminin geri kalanını devam ettirecektir.


Hayır, hiçbir şekilde engelleme yoktur. Departmana asla ulaşamayacak == async yöntemi zaten bitene kadar boş.
Steve,

1
async awaitgenellikle yeni dişler yaratmaz ve yaptıysa bile, null olup olmadığını öğrenmek için o departman değerini beklemeniz gerekir.
Robert Harvey,

2
Yani temelde söylediğiniz şey, bunun herhangi bir yararı public async Task<ActionResult> olmasıawait
Robert Harvey,

2
@RobertHarvey Evet, fikrin var. Async / Await aslında viraldir. Bir işlevi "bekler" ise, onu çağıran işlevleri de beklemelisiniz. Kilitlenmelere neden olabileceğinden, veya awaitile karıştırılmamalıdır . Eşzamansız / beklemede zinciri, genellikle olay işleyicileri için veya doğrudan UI öğeleri tarafından çağrılan işlevler için kullanılan bir imzalı bir işlevde sona ermektedir . .Wait().Resultasync void
KChaloux

1
@ Steve - " ... bu yüzden kendi başlığında olur. " Hayır, oku Görevler hala konu değil ve eşzamansız paralel değil . Bu, yeni bir iş parçacığı oluşturulmadığını gösteren gerçek çıktı içerir.
ToolmakerSteve

1

Bir sözleşme gibi "eşzamansız" düşünmeyi seviyorum, "İhtiyacınız olduğunda bunu eşzamanlı olarak uygulayabilirim, ama beni diğer eşzamanlı işlevler gibi de çağırabilirsin" diyen bir sözleşme.

Yani, bir geliştirici işlevler yapıyordu ve bazı tasarım kararları onları "async" olarak bir demet işlevi yapmaya / işaretlemeye yöneltti. Fonksiyonların arayan / tüketicisi karar verirken bunları kullanma özgürlüğüne sahiptir. Dediğiniz gibi, işlev çağrısından hemen önce çağrı bekleyebilir ve bekleyebilir, bu şekilde eşzamanlı bir işlev gibi davranabilirsiniz, ancak isterseniz, beklemeden çağrı yapamazsınız.

Task<Department> deptTask = db.Departments.FindAsync(id);

ve sonra, örneğin, aradığınız işleve 10 satır

Department d = await deptTask;

dolayısıyla asenkronize bir fonksiyon olarak ele alıyor.

Sana kalmış.


1
Daha çok "İletiyi eşzamansız olarak kullanma, sonucu almak için bunu kullanma" hakkını saklı tutar.
Deduplicator

-1

“eğer yine de hemen engelleyeceksen” cevabınız "Evet". Sadece hızlı bir cevaba ihtiyaç duyduğunuzda, bekleyen / zaman uyumsuz bir anlam ifade eder. Örneğin, UI dizisi gerçekten eşzamansız bir yönteme gelir, UI dizisi geri döner ve düğme tıklatmasını dinlemeye devam ederken, "bekliyor" ifadesinin altındaki kod diğer iş parçacığı tarafından kesilir ve son olarak sonuç alınır.


1
Bu cevap diğer cevapların vermemesini sağlayan nedir?
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.