Task.Run () ve Task.Factory.StartNew () arasındaki fark nedir


192

Yöntemim var:

private static void Method()
{
    Console.WriteLine("Method() started");

    for (var i = 0; i < 20; i++)
    {
        Console.WriteLine("Method() Counter = " + i);
        Thread.Sleep(500);
    }

    Console.WriteLine("Method() finished");
}

Ve bu yöntemi yeni bir Görevde başlatmak istiyorum. Böyle yeni bir işe başlayabilirim

var task = Task.Factory.StartNew(new Action(Method));

veya bu

var task = Task.Run(new Action(Method));

Ancak Task.Run()ve arasında herhangi bir fark var mı Task.Factory.StartNew()? Her ikisi de Görev örneğini oluşturduktan hemen sonra ThreadPool ve start Method () yöntemini kullanıyor. Ne zaman birinci varyantı ne zaman ikinci varyantı kullanmalıyız?


6
Aslında StartNew, ThreadPool'u kullanmak zorunda değil, cevabımda bağlandığım bloga bakın. Sorun StartNewvarsayılan TaskScheduler.Currentolarak iş parçacığı havuzu olabilir ama aynı zamanda UI iş parçacığı olabilir kullanımları.
Scott Chamberlain

Yanıtlar:


197

İkinci yöntem, Task.Run.NET çerçevesinin (.NET 4.5'te) sonraki bir sürümünde tanıtıldı.

Bununla birlikte, ilk yöntem, Task.Factory.StartNewoluşturmak istemediğiniz iş parçacığı hakkında birçok yararlı şey tanımlama fırsatı verirken Task.Run, bunu sağlamaz.

Örneğin, uzun süren bir görev dizisi oluşturmak istediğinizi varsayalım. İş parçacığı havuzunun bir iş parçacığı bu görev için kullanılacaksa, bu iş parçacığı havuzunun kötüye kullanılması olarak kabul edilebilir.

Bundan kaçınmak için yapabileceğiniz bir şey, görevi ayrı bir iş parçacığında çalıştırmak olacaktır. Bu göreve adanmış ve göreviniz tamamlandıktan sonra yok edilecek yeni oluşturulmuş bir iş parçacığı . Sen olamaz ile bunu başarmak Task.Runsen bunu yapabilirsiniz ederken, Task.Factory.StartNewaşağıda gibi:

Task.Factory.StartNew(..., TaskCreationOptions.LongRunning);

Belirtildiği gibi burada :

Bu nedenle, .NET Framework 4.5 Geliştirici Önizlemesi'nde yeni Task.Run yöntemini tanıttık. Bu, hiçbir şekilde Task.Factory.StartNew'i geçersiz kılmaz, ancak bir grup parametreyi belirtmeye gerek kalmadan Task.Factory.StartNew'i kullanmanın hızlı bir yolu olarak düşünülmelidir . Bu bir kısayol. Aslında, Task.Run aslında Task.Factory.StartNew için kullanılan aynı mantık açısından uygulanır, sadece bazı varsayılan parametreleri geçirir. Bir Eylemi Görev'e ilettiğinizde:

Task.Run(someAction);

tam olarak şuna eşittir:

Task.Factory.StartNew(someAction, 
    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

4
State that’s exactly equivalent totutmayan bir kod parçası var .
Emaborsa

7
@Emaborsa Bu kod parçasını gönderebilir ve argümanınızı hazırlayabilirseniz sevinirim. Şimdiden teşekkürler !
Christos

4
@Emaborsa Bir gist oluşturabilir, gist.github.com ve paylaşabilirsiniz. Ancak, bu özeti paylaşmak dışında, lütfen ifadenin tha's exactly equivalent tosahip olmadığı sonuca nasıl ulaştığınızı belirtin . Şimdiden teşekkürler. Kodunuzla ilgili yorum yaparak açıklamak güzel olurdu. Teşekkürler :)
Christos

8
Ayrıca Task.Run'un iç içe geçmiş görevi varsayılan olarak açacağından bahsetmeye değer. Büyük farklılıklar hakkında bu makaleyi okumanızı tavsiye ederim: blogs.msdn.microsoft.com/pfxteam/2011/10/24/…
Pawel Maga

1
@ The0bserver hayır, öyle TaskScheduler.Default. Lütfen buraya göz atın referanslar.microsoft.com/#mscorlib/system/threading/Tasks/… .
Christos

46

İnsanlar bundan daha önce bahsetti

Task.Run(A);

Eşittir

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

Ama kimse bundan bahsetmedi

Task.Factory.StartNew(A);

Şuna eşittir:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

Gördüğünüz gibi iki parametre için farklıdır Task.Runve Task.Factory.StartNew:

  1. TaskCreationOptions- Task.Runkullandığı TaskCreationOptions.DenyChildAttacharaçlar çocukların görevleri ebeveyne bağlı olamaz, bu saydığınız:

    var parentTask = Task.Run(() =>
    {
        var childTask = new Task(() =>
        {
            Thread.Sleep(10000);
            Console.WriteLine("Child task finished.");
        }, TaskCreationOptions.AttachedToParent);
        childTask.Start();
    
        Console.WriteLine("Parent task finished.");
    });
    
    parentTask.Wait();
    Console.WriteLine("Main thread finished.");

    Çağırdığımızda parentTask.Wait(), childTaskbeklenmemize rağmen, bunun için belirttiğimiz TaskCreationOptions.AttachedToParenthalde, bunun nedeni TaskCreationOptions.DenyChildAttachçocukların ona bağlanmasını yasaklamasıdır. Eğer aynı kodları çalıştırırsanız Task.Factory.StartNewyerine Task.Run, parentTask.Wait()bekleyecektir childTaskçünkü Task.Factory.StartNewkullanımlarTaskCreationOptions.None

  2. TaskScheduler- Task.Runkullanır TaskScheduler.Default; bu, varsayılan görev zamanlayıcısının (İş Parçacığı Havuzunda görevleri çalıştıran zamanlayıcı) her zaman görevleri çalıştırmak için kullanılacağı anlamına gelir. Task.Factory.StartNewÖte yandan TaskScheduler.Currentgeçerli iş parçacığının zamanlayıcısı anlamına gelen kullanır , TaskScheduler.Defaultancak her zaman olmayabilir . Aslında geliştirme Winformsveya WPFuygulama geliştirirken mevcut iş parçacığından kullanıcı arayüzünü güncellemek gerekir, bu kişilerin TaskScheduler.FromCurrentSynchronizationContext()görev zamanlayıcısını kullanması gerekir , istemeden TaskScheduler.FromCurrentSynchronizationContext()zamanlayıcı kullanılan görev içinde uzun süre çalışan başka bir görev oluşturursanız , kullanıcı arayüzü dondurulur. Bunun daha ayrıntılı bir açıklamasını burada bulabilirsiniz

Bu nedenle, genellikle iç içe geçmiş çocuklar görevini kullanmıyorsanız ve görevlerinizin her zaman İş Parçacığı Havuzu'nda yürütülmesini Task.Runistiyorsanız, daha karmaşık senaryolarınız yoksa kullanmak daha iyidir .


1
Bu harika bir ipucu, kabul edilen cevap olmalı
Ali Bayat

30

Farkı açıklayan bu blog makalesine bakın . Temelde:

Task.Run(A)

Yapmakla aynı şey:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);   

28

Task.RunYeni .NET framework sürümü tanıtıldı var ve onu olduğu önerilir .

.NET Framework 4.5'ten başlayarak, bilgisayara bağlı bir görevi başlatmanın önerilen yolu Task.Run yöntemidir. StartNew yöntemini yalnızca uzun süren, işlemle ilişkili bir görev için ayrıntılı denetim gerektiğinde kullanın.

Daha Task.Factory.StartNewfazla seçenek var, Task.Runbir stenografi:

Çalıştır yöntemi, bir görevi varsayılan değerleri kullanarak başlatmayı kolaylaştıran bir dizi aşırı yük sağlar. StartNew aşırı yüklerine hafif bir alternatiftir.

Kısaca kısaca teknik bir kısayol demek istiyorum :

public static Task Run(Action action)
{
    return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
        TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}

21

Stephen Cleary'nin bu yazısına göre Task.Factory.StartNew () tehlikeli:

Bloglar ve bir arka plan iş parçacığı üzerinde çalışma açmak için Task.Factory.StartNew kullanan SO sorularda bir sürü kod görüyorum. Stephen Toub, Task.Run'un neden Task.Factory.StartNew'den daha iyi olduğunu açıklayan mükemmel bir blog makalesine sahiptir, ancak bence birçok insan bunu okumamış (veya anlamıyor). Aynı argümanları aldım, biraz daha güçlü bir dil ekledim ve bunun nasıl gittiğini göreceğiz. :) StartNew, Task.Run'dan çok daha fazla seçenek sunuyor, ancak göreceğimiz gibi oldukça tehlikeli. Zaman uyumsuz kodda Task.Run yerine Task.Factory.StartNew tercih etmelisiniz.

İşte gerçek nedenler:

  1. Zaman uyumsuz delegeleri anlamıyor. Bu aslında StartNew'i kullanmak istemenizin nedenlerinde 1. nokta ile aynıdır. Sorun şudur: StartNew'e zaman uyumsuz bir temsilci ilettiğinizde, döndürülen görevin bu temsilci olduğunu temsil etmesi doğaldır. Bununla birlikte, StartNew zaman uyumsuz delegeleri anlamadığından, bu görevin gerçekte temsil ettiği şey o temsilci için sadece bir başlangıçtır. Bu, kodlayıcıların zaman uyumsuz kodda StartNew kullanırken karşılaştığı ilk tuzaklardan biridir.
  2. Kafa karıştırıcı varsayılan zamanlayıcı. Tamam, hile soru zamanı: Aşağıdaki kodda, “A” yöntemi hangi iş parçacığında çalışıyor?
Task.Factory.StartNew(A);

private static void A() { }

Bunun hileli bir soru olduğunu biliyorsun, değil mi? “Bir iş parçacığı havuzu iş parçacığı” yanıtladıysanız, üzgünüm, ama bu doğru değil. “A” şu anda TaskScheduler'ın yürüttüğü her şeyde çalışacaktır!

Bu, bir işlem tamamlandığında potansiyel olarak UI iş parçacığında çalışabileceği ve Stephen Cleary'nin görevinde daha tam olarak açıkladığı gibi bir devam nedeniyle UI iş parçacığına geri çekileceği anlamına gelir.

Benim durumumda, aynı zamanda meşgul bir animasyon görüntülerken bir görünüm için bir veri ızgarası yüklerken görevleri arka planda çalıştırmaya çalışıyordum. Meşgul animasyon kullanılırken Task.Factory.StartNew()görüntülenmedi ancak geçiş yaptığımda animasyon düzgün görüntüleniyor Task.Run().

Ayrıntılar için lütfen https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html adresine bakın.


1

Task.Factory.StartNew () için bir kısayol olması gibi Task.Run () benzerliklerinin yanı sıra, senkronizasyon ve zaman uyumsuz delegeler durumunda davranışları arasında bir dakika farkı vardır.

Aşağıdaki iki yöntem olduğunu varsayalım:

public async Task<int> GetIntAsync()
{
    return Task.FromResult(1);
}

public int GetInt()
{
    return 1;
}

Şimdi aşağıdaki kodu göz önünde bulundurun.

var sync1 = Task.Run(() => GetInt());
var sync2 = Task.Factory.StartNew(() => GetInt());

Burada hem sync1 hem de sync2 Görev <int> türündedir.

Ancak, zaman uyumsuz yöntemlerde fark gelir.

var async1 = Task.Run(() => GetIntAsync());
var async2 = Task.Factory.StartNew(() => GetIntAsync());

Bu senaryoda, async1 Görev <int> türünde, ancak async2 Görev <Görev <int>> türünde


Yeap, çünkü yöntemin çözülme Task.Runişlevselliğine sahiptir Unwrap. İşte bu kararın ardındaki nedeni açıklayan bir blog yazısı.
Theodor Zoulias

-8

İki hizmet çağrıları Benim uygulamada ben Task.Run ve her iki karşılaştırıldı Task.Factory.StartNew . Benim durumumda her ikisinin de iyi çalıştığını gördüm. Ancak ikincisi daha hızlı.


Doğru ya da yararlı olmasa bile, bu cevabın neden "10" aşağı oyu hak ettiğini bilmiyorum ...
Mayer Spitzer
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.