Arayüzümün Görev'e dönmesi gerekiyorsa, işlem yapmama uygulamasının en iyi yolu nedir?


435

Aşağıdaki kodda, arabirim nedeniyle, sınıfın LazyBaryönteminden bir görev döndürmesi gerekir (ve sake argümanları değiştirilemez). Eğer LazyBaryönteminden bir No-Operasyon görevi döndürmek için en iyi yolu nedir - s uygulaması hızla ve eşzamanlı çalışmasına olur sıradışı mı?

Birlikte gitmiş Task.Delay(0)ancak işlevi denir, bu herhangi bir performans yan etkileri varsa bilmek istiyorum, aşağıda çok (kez ikinci yüzlerce argümanlar uğruna, derler):

  • Bu sözdizimsel şeker büyük bir şeye yöneliyor mu?
  • Uygulamamın iş parçacığı havuzunu tıkamaya mı başlıyor?
  • Derleyici Delay(0)farklı şekilde başa çıkmak için yeterli mi?
  • Misiniz return Task.Run(() => { });farklı olsun?

Daha iyi bir yol var mı?

using System.Threading.Tasks;

namespace MyAsyncTest
{
    internal interface IFooFace
    {
        Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
    }

    /// <summary>
    /// An implementation, that unlike most cases, will not have a long-running
    /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
    /// </summary>
    internal class LazyBar : IFooFace
    {
        #region IFooFace Members

        public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
            // First, do something really quick
            var x = 1;

            // Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
            // Is it a real no-op, or if I call this a lot, will it adversely affect the
            // underlying thread-pool? Better way?
            return Task.Delay(0);

            // Any different?
            // return Task.Run(() => { });

            // If my task returned something, I would do:
            // return Task.FromResult<int>(12345);
        }

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
        }

        private static async void Test()
        {
            IFooFace foo = FactoryCreate();
            await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
            return;
        }

        private static IFooFace FactoryCreate()
        {
            return new LazyBar();
        }
    }
}


8
Şahsen ben giderdim Task.FromResult<object>(null).
CodesInChaos

Yanıtlar:


625

Kullanılması Task.FromResult(0)veya Task.FromResult<object>(null)bir oluşturma daha az yük tabidir Taskno-op ifadesi ile. TaskÖnceden belirlenmiş bir sonuç ile bir oluştururken , herhangi bir zamanlama yükü söz konusu değildir.


Bugün, bunu yapmak için Task.CompletedTask kullanmanızı tavsiye ederim .


5
Eğer github.com/StephenCleary/AsyncEx kullanıyorsanız , bu tamamlanmış görevleri diğer oldukça kullanışlı olanlarla (0 int, true / false, Varsayılan <T> ()) sağlamak için bir TaskConstants sınıfı sağlarlar
quentin-starin

5
return default(YourReturnType);
Efsaneler

8
@Legends Doğrudan bir Görev oluşturmak için işe yaramıyor
Reed Copsey

18
emin değilim ama Task.CompletedTaskhile yapabilir! (ancak .net 4.6 gerektirir)
Peter

187

Eklemek için Reed Copsey cevabı kullanma hakkında Task.FromResulttamamlanan görevlerin tüm örnekleri aynı olduğundan zaten tamamlanmış bir görevi önbelleğe eğer daha performansını artırabilir,:

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

İle TaskExtensions.CompletedTaskaynı uygulamayı tüm uygulama etki alanında kullanabilirsiniz.


.Net Framework (v4.6) en son sürümünü ekler sadece bununla ilgili Task.CompletedTaskstatik özelliği

Task completedTask = Task.CompletedTask;

Ben gerekir mi dönmek o ya beklemektedir üzerine?
Pixar

@Pixar ne demek istiyorsun? Her ikisini de yapabilirsiniz, ancak bekleyen senkronize devam edecektir.
i3arnon

Üzgünüm, durumlarından bahsetmek zorunda :) Bunu şimdi anlıyorum, biz yapabilir public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()hem de public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations(). Yani, ya return CompletedTask;ya await CompletedTask;. Daha çok tercih edilen nedir (belki daha verimli veya daha uyumlu)?
Pixar

3
@Pixar Temizlemiyorum. "'Zaman uyumsuz' daha verimli olur" demek istedim. Async yönteminin yapılması derleyiciye durum makinesine dönüştürmesini bildirir. Ayrıca, her aradığınızda yeni bir görev oluşturur. Zaten tamamlanmış bir görevin geri gönderilmesi daha açık ve daha performanslı olacaktır.
i3arnon

3
@Asad tahsisleri azaltır (ve onunla GC zamanı). Tamamlanan bir Göreve her ihtiyacınız olduğunda yeni bellek atamak ve bir Görev örneği oluşturmak yerine, bunu yalnızca bir kez yaparsınız.
i3arnon

38

Task.Delay(0)Kabul edilen cevapta olduğu gibi tamamlanmış bir önbelleğe alınmış kopya olduğu için iyi bir yaklaşımdı Task.

4.6 itibariyle artık Task.CompletedTaskamacı daha açıktır, ancak Task.Delay(0)yine de yalnızca tek bir önbelleğe alınmış örnek döndürmekle kalmaz , aynı önbelleğe alınmış tek örneği de döndürür Task.CompletedTask.

Kullanımını ne önbelleğe alınmış doğa sabiti kalması garanti edilir, ancak yalnızca uygulama bağımlı optimizasyonlar gibidir uygulama bağımlı optimizasyonlar olarak (uygulama hala geçerli olduğu bir şey değişirse olduğunu, hala düzgün çalışması ediyorum) Task.Delay(0)idi kabul edilen cevaptan daha iyi.


1
Hala 4.5 kullanıyorum ve biraz araştırma yaptığımda Task.Delay (0) 'ın statik bir CompletedTask üyesi döndürmek için özel kasalı olduğunu görmek için eğlendim. Hangi sonra kendi statik CompletedTask üye önbelleğe. : P
Darren Clark

2
Neden bilmiyorum, ancak Task.CompletedTaskPCL projesinde kullanılamıyor, .net sürümünü 4.6'ya (profil 7) ayarlamış olsam bile, VS2017'de test edildi.
Felix

@Fay PCL API yüzeyinin bir parçası olmamalı sanırım, ancak tek şey şu anda bir şey yapmak PCL destekleyen 4.5 de destekler, bu yüzden zaten Task.CompletedTask => Task.Delay(0);bunu desteklemek için kendi kullanmak zorunda , bu yüzden don Kesinlikle kafamın üstünden bilmiyorum.
Jon Hanna

17

Son zamanlarda bu karşılaştım ve geçersiz yöntem hakkında uyarı / hata almaya devam etti.

Derleyiciyi yerleştirme işindeyiz ve bu temizler:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

Bu, şimdiye kadarki tüm tavsiyelerin en iyisini bir araya getiriyor. Aslında yöntemde bir şey yapmadıkça bir iade ifadesi gerekli değildir.


17
Bu tamamen yanlış. Yöntem tanımı async içerdiğinden derleyici hatası alıyorsunuz, bu nedenle derleyici sizi bekliyor. "Doğru" kullanım herkese açık olacaktır. Görev MyVoidAsyncMethog () {return Task.CompletedTask;}
Keith

3
Bu en temiz cevap gibi görünüyor çünkü bu aşağı oy verildi emin değilim
webwake

3
Keith'in yorumu yüzünden.
noelicus

4
Tamamen yanlış değil, async anahtar kelimesini yeni kaldırdı. Benim yaklaşımım daha deyimsel. Minimalist. Biraz kaba değilse.
Alexander Trauzzi

1
Bu bir anlam ifade etmiyor, burada Keith ile tamamen aynı fikirdeyim. Neden gerekli olmayan kodu eklemelisiniz? public Task MyVoidAsyncMethod() {}yukarıdaki yöntemle tamamen aynıdır. Bu şekilde kullanmak için bir kullanıcı tabanı varsa, lütfen ek kodu ekleyin.
Nick N.

12
return Task.CompletedTask; // this will make the compiler happy

9

Belirtilen türü döndürmeniz gerektiğinde:

Task.FromResult<MyClass>(null);

3

Task completedTask = Task.CompletedTask;Net 4.6 çözümünü tercih ederim , ancak başka bir yaklaşım yöntemi zaman uyumsuz olarak işaretlemek ve void döndürmektir:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

Bir uyarı alırsınız (ifadeyi beklemeden CS1998 - Async işlevi), ancak bu bağlamda yoksaymak güvenlidir.


1
Yönteminiz geçersiz döndürürse, istisnalarla ilgili sorunlarınız olabilir.
Adam Tuliper - MSFT

0

Jenerikler kullanıyorsanız, tüm cevaplar bize derleme hatası verecektir. Kullanabilirsiniz return default(T);. Daha fazla açıklamak için aşağıdaki örnek.

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }

Neden inişli çıkışlı?
Karthikeyan VK

Soru asenkron yöntemler hakkında değildi :)
Frode Nilsen

0
return await Task.FromResult(new MyClass());

3
Bu kod soruyu çözebilir, ancak bunun sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olabilir ve muhtemelen daha fazla oyla sonuçlanır. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın. Lütfen açıklama eklemek için yanıtınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
David Buck
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.