"Uyarı CS4014: Bu çağrı beklenmediği için mevcut yöntemin yürütülmesi devam ediyor ..."


156

Bu "C # içinde bir async yöntemini güvenli bir şekilde beklemeksizin nasıl çağırılır" kopyası değildir .

Aşağıdaki uyarıyı nasıl güzel bir şekilde engelleyebilirim?

CS4014 uyarısı: Bu çağrı beklenmediğinden, çağrı tamamlanmadan önce geçerli yöntemin yürütülmesi devam eder. Aramanın sonucuna 'bekliyor' operatörünü uygulamayı düşünün.

Basit bir örnek:

static async Task WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // I want fire-and-forget 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Ne denedim ve beğenmedim:

static async Task StartWorkAsync()
{
    #pragma warning disable 4014
    WorkAsync(); // I want fire-and-forget here
    #pragma warning restore 4014
    // ...
}

static async Task StartWorkAsync()
{
    var ignoreMe = WorkAsync(); // I want fire-and-forget here
    // ...
}

Güncellenmiş , orijinal kabul edilen cevap düzenlendiğinden, burada uygun olduğunu düşünmediğim için kabul edilen cevabı C # 7.0 kartlarını kullanarak değiştirdim ContinueWith. Ateşle ve unut işlemleri için istisnaları günlüğe kaydetmem gerektiğinde, burada Stephen Cleary tarafından önerilen daha ayrıntılı bir yaklaşım kullanıyorum .


1
Sence #pragmahoş değil mi?
Frédéric Hamidi

10
@ FrédéricHamidi, biliyorum.
noseratio

2
@Noseratio: Ah, doğru. Üzgünüm, diğer uyarı olduğunu düşünmüştüm. Beni görmezden gel!
Jon Skeet

3
@Terribad: Gerçekten emin değilim - uyarının çoğu durumda oldukça makul olduğu görülüyor. Özellikle, herhangi bir arızada ne olmasını istediğinizi düşünmelisiniz - genellikle "ateşle ve unut" için bile arızaların nasıl kaydedileceğini vb.
Hesaplamalısınız

4
Eğer bu şekilde ya da başka kullanmadan önce @Terribad, istisnanın (kontrol zaman uyumsuz yöntemleri için yayılır nasıl bir net bir resim olmalıdır bu ). Ardından, Knaģis @ tarafından cevap zarif bir yol sağlar değil bir yoluyla, yangın ve unut için herhangi bir istisna kaybetmeden async voidyardımcı yöntemiyle.
noseratio

Yanıtlar:


160

C # 7 ile artık kartlarınızı kullanabilirsiniz :

_ = WorkAsync();

7
Bu, hatırlayamadığım kullanışlı bir küçük dil özelliğidir. Beynimde bir varmış gibi _ = ....
Marc

3
Bir SupressMessage, uyarımı Visual Studio "Hata Listemden" çıkardı, ancak "Çıktı" değil buldum ve atılandan #pragma warning disable CSxxxxdaha çirkin görünüyor;)
David Savage

122

Uyarıyı önleyecek bir uzantı yöntemi oluşturabilirsiniz. Uzantı yöntemi boş olabilir veya .ContinueWith()orada istisna işleme ekleyebilirsiniz .

static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        task.ContinueWith(
            t => { WriteLog(t.Exception); },
            TaskContinuationOptions.OnlyOnFaulted);
    }
}

public async Task StartWorkAsync()
{
    this.WorkAsync().Forget();
}

Ancak, ASP.NET çalışan görevlerin sayısını sayar, bu nedenle Forget()yukarıda listelenen basit uzantıyla çalışmaz ve bunun yerine istisna ile başarısız olabilir:

Bir eşzamansız işlem hala beklemedeyken eşzamansız bir modül veya işleyici tamamlandı.

.NET 4.5.2 ile aşağıdakiler kullanılarak çözülebilir HostingEnvironment.QueueBackgroundWorkItem:

public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
    return task.ContinueWith(
        t => { WriteLog(t.Exception); },
        cancelToken,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.Default);
}

public async Task StartWorkAsync()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
        cancelToken => this.WorkAsync().HandleFault(cancelToken));
}

8
Buldum TplExtensions.Forget. Altında çok daha fazla iyilik var Microsoft.VisualStudio.Threading. Keşke Visual Studio SDK dışında kullanıma sunulmuş olsaydı.
noseratio

1
@Noseratio ve Knagis, bu yaklaşımı seviyorum ve kullanmayı planlıyorum. İlgili bir takip sorusu gönderdim: stackoverflow.com/questions/22864367/fire-and-forget-approach
Matt Smith

3
@stricq Forget () hizmetine ConfigureAwait (false) eklemenin amacı nedir? Anladığım kadarıyla, ConfigureAwait yalnızca bir Görevde beklemenin kullanıldığı noktada iş parçacığı eşitlemesini etkiler, ancak Forget () 'ın amacı Görevi atmaktır, bu nedenle Görev asla beklenemez, bu nedenle burada ConfigureAwait anlamsızdır.
dthorpe

3
Yumurtlama iş parçacığı yangın ve unutma görevi tamamlanmadan önce kaybolursa, ConfigureAwait (false) olmadan yine de yumurtlama iş parçacığına geri adım atmaya çalışır, bu iş parçacığı gittikçe kilitlenir. ConfigureAwait (false) ayarı, sisteme çağıran evreye geri dönmemesini bildirir.
stricq

2
Bu yanıtta belirli bir vakayı yönetmek için bir düzenleme ve bir düzine yorum var. Basitçe işler çoğu zaman doğru şeylerdir, ıskartaya çıkın! Ve ben @ fjch1997 cevap alıntı: Sadece bir uyarı bastırmak amacıyla, yürütmek için birkaç keneler alır bir yöntem oluşturmak aptalca.
Teejay

39

Yöntemi aşağıdaki öznitelikle dekore edebilirsiniz:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
    WorkAsync();
    // ...
}

Temel olarak derleyiciye ne yaptığınızı bildiğinizi söylüyorsunuz ve olası hata hakkında endişelenmenize gerek yok.

Bu kodun önemli kısmı ikinci parametredir. "CS4014:" kısmı uyarıyı baskılayan kısımdır. Gerisini istediğiniz her şeyi yazabilirsiniz.


Benim için çalışmıyor: Mac 7.0 için Visual Studio 7.0.1 (yapı 24). Öyle görünüyor ama - hayır.
IronRod

1
[SuppressMessage("Compiler", "CS4014")]Hata Listesi penceresindeki iletiyi bastırır, ancak Çıktı penceresi hala bir uyarı satırı gösterir
David Ching

35

Bununla baş etmenin iki yolu.

Silme değişkenine kaydetme (C # 7)

Misal

_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);

C # 7'de atılanlardan beri, şimdi bu uyarıyı bastırmaktan daha iyi olduğunu düşünüyorum. Çünkü sadece uyarıyı bastırmakla kalmaz, aynı zamanda ateş ve unut niyetini de netleştirir.

Dahası, derleyici onu serbest bırakma modunda optimize edebilecektir.

Sadece bastır

#pragma warning disable 4014
...
#pragma warning restore 4014

"ateş ve unut" için yeterince güzel bir çözümdür.

Bu uyarının var olmasının nedeni, çoğu durumda görevi beklemeden döndüren bir yöntemi kullanma niyetiniz olmamasıdır. Ateş etmeyi ve unutmayı planladığınızda uyarıyı bastırmak mantıklıdır.

Nasıl yazıldığını hatırlamakta sorun yaşıyorsanız, #pragma warning disable 4014Visual Studio'nun sizin için eklemesine izin verin. Ctrl + tuşlarına basın. "Hızlı İşlemler" i ve ardından "CS2014'ü Gizle" yi açmak için

Neticede

Sadece bir uyarıyı bastırmak amacıyla, yürütülmesi gereken birkaç nokta daha gerektiren bir yöntem oluşturmak aptalca.


Bu, Mac 7.0.1 için Visual Studio'da (yapı 24) çalıştı.
IronRod

1
Sadece bir uyarıyı bastırmak amacıyla yürütmek için birkaç keneyi daha gerektiren bir yöntem oluşturmak aptalca - bu hiç ekstra keneler [MethodImpl(MethodImplOptions.AggressiveInlining)] void Forget(this Task @this) { } /* ... */ obj.WorkAsync().Forget();
eklemiyor

1
@Noseratio AggressiveInliningDerleyiciyi kullandığımda çoğu zaman sadece herhangi bir nedenle yok sayar
fjch1997

1
Ben pragma seçeneği gibi, çünkü süper basit ve sadece geçerli satır (veya bölüm) için geçerli, bütün bir yöntem değil.
wasatchwizard

2
Daha #pragma warning disable 4014sonra uyarıyı geri yüklemek için gibi ve sonra hata kodunu kullanmayı unutmayın #pragma warning restore 4014. Hala hata kodu olmadan çalışır, ancak hata numarasını eklemezseniz tüm mesajları bastırır.
DunningKrugerEffect

11

Uyarıyı durdurmanın kolay bir yolu, Görevi çağırırken atamaktır:

Task fireAndForget = WorkAsync(); // No warning now

Ve böylece orijinal yayında şunları yaparsınız:

static async Task StartWorkAsync()
{
    // Fire and forget
    var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Özellikle benim sevmediğim konulardan biri olarak sorunun kendisinde bu yaklaşımdan bahsettim.
noseratio

Tüh! Fark etmedim, çünkü pragma'nızla aynı kod bölümünde ... Ve cevaplar arıyordum. Bunun dışında bu yöntemle ilgili hoşunuza gitmeyen şey nedir?
noelicus

1
Ben öyle yapmak taskbir unutulmuş yerel değişken gibi görünüyor. Derleyicinin bana başka bir uyarı vermesi gerektiği gibi, " taskatanır ancak değeri asla kullanılmaz" gibi bir şey , bunun yanında yapmaz. Ayrıca, kodu daha az okunabilir hale getirir. Bu yaklaşımı kendim kullanıyorum .
noseratio

Yeterince adil - benzer bir duygum vardı, bu yüzden ona isim veriyorum fireAndForget... bu yüzden bundan sonra referanssız olmasını bekliyorum.
noelicus

4

Uyarının nedeni, WorkAsync'in Taskasla okunmayan veya beklenen bir döndürmüyor olmasıdır . WorkAsync'in dönüş türünü şu şekilde ayarlayabilirsiniz:void ; uyarı .

Genellikle bir yöntem Task, arayan kişinin işçinin durumunu bilmesi gerektiğinde a değerini döndürür . Bir ateşle ve unut durumunda, arayanın çağrılan yöntemden bağımsız olduğuna benzemek için boşluk döndürülmelidir.

static async void WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // no warning since return type is void

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

2

Neden void döndüren bir zaman uyumsuz yöntemin içine sarmıyorsunuz? Biraz uzun ama tüm değişkenler kullanılıyor.

static async Task StartWorkAsync()
{   
     async void WorkAndForgetAsync() => await WorkAsync();
     WorkAndForgetAsync(); // no warning
}

1

Bu yaklaşımı bugün kazara buldum. Bir temsilci tanımlayabilir ve ilk olarak temsilciye asenkron yöntem atayabilirsiniz.

    delegate Task IntermediateHandler();



    static async Task AsyncOperation()
    {
        await Task.Yield();
    }

ve öyle söyle

(new IntermediateHandler(AsyncOperation))();

...

Delege kullanırken derleyicinin aynı uyarıyı vermemesinin ilginç olduğunu düşündüm.


Temsilci ilan etmeye gerek yok, (new Func<Task>(AsyncOperation))()IMO hala biraz fazla ayrıntılı olsa da yapabilirsiniz .
noseratio
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.