Zaman uyumsuz yöntemin tamamlanması nasıl beklenir?


138

Verileri bir USB HID sınıfı cihaza aktaran bir WinForms uygulaması yazıyorum. Benim uygulama burada bulunabilir mükemmel Genel HID kitaplığı v6.0 kullanır . Özetle, cihaza veri yazmak gerektiğinde, bu çağrılan kod:

private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        RequestToGetInputReport();
    }
}

Kodum while döngüsünün dışına çıktığında, aygıttan bazı verileri okumam gerekiyor. Ancak, cihaz hemen yanıt veremiyor, bu yüzden devam etmeden önce bu aramanın geri dönmesini beklemem gerekiyor. Şu anda var olduğundan, RequestToGetInputReport () şu şekilde bildirilir:

private async void RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

Değer için, GetInputReportViaInterruptTransfer () bildirimi şöyle görünür:

internal async Task<int> GetInputReportViaInterruptTransfer()

Ne yazık ki, .NET 4.5'teki yeni zaman uyumsuz / beklemede olan teknolojilerin çalışmalarını çok iyi bilmiyorum. Daha önce await anahtar kelime hakkında biraz okuma yaptım ve bana GetInputReportViaInterruptTransfer () çağrısında RequestToGetInputReport () içinde beklemek (ve belki de?) Çağrısı gibi görünüyordu ama RequestToGetInputReport () çağrısı gibi görünüyordu Kendisi bekliyor çünkü while döngüsüne hemen giriyorum?

Gördüğüm davranışı açıklığa kavuşturan var mı?

Yanıtlar:


131

Kaçının async void. TaskBunun yerine yöntemlerinizi iade ettirin void. Sonra awaitonları yapabilirsiniz .

Bunun gibi:

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        await RequestToGetInputReport();
    }
}

private async Task RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

1
Çok güzel teşekkür ederim. Ben de benzer bir konuda kafamı çizilmeye ve fark değiştirmek oldu voidiçin Tasksize söylediği gibi sadece.
Jeremy

8
Bu küçük bir şeydir, ancak konvansiyonu takip etmek için her iki yöntemin de Async isimlerine eklenmesi gerekir, örneğin RequestToGetInputReportAsync ()
timtam

6
ve eğer arayan Ana işlevi ise?
symbiont

14
@symbiont: O zaman kullanGetAwaiter().GetResult()
Stephen Cleary

4
@AhmedSalah Yöntemin Taskyürütülmesini temsil eder - böylece returndeğerler yerleştirilir Task.Resultve istisnalar yerleştirilir Task.Exception. İle void, derleyicinin istisnalar için hiçbir yeri yoktur, bu yüzden sadece bir iş parçacığı havuzu iş parçacığı üzerinde yeniden yükseltilir.
Stephen Cleary

229

En önemli şey hakkında bilmek asyncve awaitolmasıdır await gelmez tamamlanmasını ilişkili çağrı bekleyin. Yapılması awaitgereken , işlemin tamamlanmış olması durumunda işlemin sonucunu derhal ve eşzamanlı olarak döndürmek veya yapmadıysa , asyncyöntemin geri kalanını yürütmek için bir devam zamanlaması yapmak ve ardından denetimi arayana geri döndürmektir. Zaman uyumsuz işlem tamamlandığında, zamanlanmış tamamlama yürütülür.

Sorunuzun başlığındaki belirli sorunun cevabı, uygun bir yöntem çağırarak bir yöntemin asyncdönüş değerini (tür Taskveya olması gereken Task<T>) engellemektir Wait:

public static async Task<Foo> GetFooAsync()
{
    // Start asynchronous operation(s) and return associated task.
    ...
}

public static Foo CallGetFooAsyncAndWaitOnResult()
{
    var task = GetFooAsync();
    task.Wait(); // Blocks current thread until GetFooAsync task completes
                 // For pedagogical use only: in general, don't do this!
    var result = task.Result;
    return result;
}

Bu kod parçası olarak, CallGetFooAsyncAndWaitOnResulta, senkron asenkron bir yöntem etrafında sarıcı GetFooAsync. Bununla birlikte, bu desen çoğunlukla, eşzamansız işlem süresince tüm bir iş parçacığı havuzu iş parçacığını engelleyeceğinden kaçınılmalıdır. Bu, API'lerin maruz kaldığı ve bunları sağlamak için büyük çaba harcayan çeşitli asenkron mekanizmaların verimsiz bir kullanımı.

"Bekliyor" sorusunun yanıtı, bu anahtar kelimelerin birkaç, daha ayrıntılı açıklamasını içerir.

Bu arada, @Stephen Cleary'nin async voidbekletmelerle ilgili rehberliği . Neden diğer güzel açıklamalar http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ ve https://jaylee.org/archive/ adresinde bulunabilir. 2012/07/08 / c-keskin zaman uyumsuz-ipuçları-ve-trick-part-2-async-void.html


18
Ben awaitbir "asenkron bekleme" olarak düşünmek (ve konuşmak) için yararlı buluyorum - yani, yöntemi (gerekirse) engeller ama iplik değil . Engelleme beklemesine rağmen RequestToSendOutputReport"beklemek" hakkında konuşmak mantıklı . RequestToGetInputReport
Stephen Cleary

@Richard Cook - ek açıklama için çok teşekkür ederim!
bmt22033

10
Bu, kabul edilen cevap olmalıdır, çünkü asıl soruya daha açık bir şekilde cevap verir (yani bir async yönteminde nasıl iş parçacığıyla blok yapılacağını).
csvan

en iyi çözüm, görev tamamlanana kadar async'i beklemek var sonuç = Task.Run (async () => {return aMethod ();}). Sonuç;
Ram chittala

70

AsynMethod'u görevi tamamlayana kadar beklemek için en iyi çözüm

var result = Task.Run(async() => await yourAsyncMethod()).Result;

15
Veya bu zaman uyumsuz "void" için: Task.Run (async () => {aAs aAccMethod ();}). Wait ();
Jiří Herník

1
Bunun sizin AsyncMethod () yöntemine göre avantajı nedir?
Justin J Stark

1
Basitçe .Result özelliğine erişmek, görevin yürütülmesi bitene kadar beklemez. Aslında, bir görev tamamlanmadan çağrılırsa bir istisna getirdiğine inanıyorum. Bu bir Task.Run () çağrısında sarma avantajı, Richard Cook aşağıda belirtildiği gibi, "beklemek" aslında bir görevin tamamlanmasını beklemiyor, ancak bir .Wait () çağrısı kullanarak tüm iş parçacığı havuzu engeller . Bu, ayrı bir iş parçacığında (zaman uyumlu olarak) bir eşzamansız yöntem çalıştırmanıza izin verir. Biraz kafa karıştırıcı, ama işte burada.
Lucas Leblanc

orada sonuçta güzel atma, sadece ihtiyacım olan şey
Gerry

Hızlı hatırlatma ECMA7 özellikli assync () veya ECMA7 öncesi ortamda çalışmayacak.
Mbotet

0

Bayrak kullanarak bir geçici çözüm:

//outside your event or method, but inside your class
private bool IsExecuted = false;

private async Task MethodA()
{

//Do Stuff Here

IsExecuted = true;
}

.
.
.

//Inside your event or method

{
await MethodA();

while (!isExecuted) Thread.Sleep(200); // <-------

await MethodB();
}

-1

Görev tamamlanana kadar beklemek için Wait () koyun

GetInputReportViaInterruptTransfer().Wait();


Bu, geçerli iş parçacığını engeller. Yani bu genellikle kötü bir şeydir.
Pure.Krome

-5

Aşağıdaki snippet, arayan kişiye geri dönmeden önce beklenen yöntemin tamamlandığından emin olmanın bir yolunu gösterir. ANCAK, bunun iyi bir uygulama olduğunu söyleyemem. Aksini düşünüyorsanız lütfen cevabımı açıklamalarla düzenleyin.

public async Task AnAsyncMethodThatCompletes()
{
    await SomeAsyncMethod();
    DoSomeMoreStuff();
    await Task.Factory.StartNew(() => { }); // <-- This line here, at the end
}

await AnAsyncMethodThatCompletes();
Console.WriteLine("AnAsyncMethodThatCompletes() completed.")

Downvoters, cevabı istediğim gibi açıklamak ister misin? Çünkü bu bildiğim kadarıyla çalışıyor ...
Jerther

3
Sorun tek yolu yapabileceğiniz olmasıdır await+ Console.WriteLinebir hale gereğidir Taskikisi arasındaki kontrolünü veren. böylece 'çözümünüz' nihayetinde Task<T>sorunu çözmeyen bir sonuç verecektir . Bir Task.Waitirade yapmak aslında işlemeyi durduracaktır (kilitlenme olasılıkları vb. İle). Başka bir deyişle, awaitaslında beklemez, sadece eşzamansız olarak çalıştırılabilir iki bölümü tek bir şekilde birleştirir Task(birisinin izleyebileceği veya bekleyebileceği)
Ruben Bartelink

-5

Aslında bunu IAsyncAction döndüren işlevler için daha yararlı buldum.

            var task = asyncFunction();
            while (task.Status == AsyncStatus.Completed) ;
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.