Apa perbedaan antara Task.Run () dan Task.Factory.StartNew ()

192

Saya punya Metode:

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");
}

Dan saya ingin memulai metode ini dalam Tugas baru. Saya dapat memulai tugas baru seperti ini

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

atau ini

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

Tetapi apakah ada perbedaan antara Task.Run()dan Task.Factory.StartNew(). Keduanya menggunakan ThreadPool dan memulai Metode () segera setelah membuat instance dari Tugas. Kapan kita harus menggunakan varian pertama dan kapan kedua?

Sergiy Lichenko
sumber
6
Sebenarnya, StartNew tidak harus menggunakan ThreadPool, lihat blog yang saya tautkan dalam jawaban saya. Masalahnya adalah StartNewpenggunaan default TaskScheduler.Currentyang mungkin merupakan kumpulan utas tetapi juga bisa menjadi utas UI.
Scott Chamberlain
2
Kemungkinan duplikat terkait penggunaan Task.Start (), Task.Run () dan Task.Factory.StartNew ()
Ahmed Abdelhameed

Jawaban:

197

Metode kedua Task.Run,, telah diperkenalkan di versi terbaru dari kerangka .NET (dalam .NET 4.5).

Namun, metode pertama Task.Factory.StartNew,, memberi Anda kesempatan untuk mendefinisikan banyak hal bermanfaat tentang utas yang ingin Anda buat, sementara Task.Runtidak menyediakan ini.

Misalnya, katakanlah Anda ingin membuat utas tugas yang berjalan lama. Jika utas kumpulan utas akan digunakan untuk tugas ini, maka ini bisa dianggap penyalahgunaan utas utas.

Satu hal yang dapat Anda lakukan untuk menghindari ini adalah menjalankan tugas di utas terpisah. Utas yang baru dibuat yang akan didedikasikan untuk tugas ini dan akan dihancurkan begitu tugas Anda selesai. Anda tidak dapat mencapai ini dengan Task.Run, sementara Anda dapat melakukannya dengan Task.Factory.StartNew, seperti di bawah ini:

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

Seperti yang dinyatakan di sini :

Jadi, di Pratinjau Pengembang .NET Framework 4.5, kami telah memperkenalkan metode Task.Run yang baru. Ini sama sekali tidak menghapus Task.Factory.StartNew, tetapi harus dianggap sebagai cara cepat untuk menggunakan Task.Factory.StartNew tanpa perlu menentukan banyak parameter. Ini jalan pintas. Bahkan, Task.Run sebenarnya diimplementasikan dalam hal logika yang sama yang digunakan untuk Task.Factory.StartNew, hanya meneruskan beberapa parameter default. Saat Anda memberikan Action ke Task.Run:

Task.Run(someAction);

itu persis sama dengan:

Task.Factory.StartNew(someAction, 
    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Christos
sumber
4
Saya memiliki sepotong kode di mana pernyataan that’s exactly equivalent toitu tidak berlaku.
Emaborsa
7
@Emaborsa Saya sangat menghargai jika Anda bisa memposting kode ini dan menguraikan argumen Anda. Terima kasih sebelumnya !
Christos
4
@Emaborsa Anda dapat membuat intisari, gist.github.com , dan membagikannya. Namun, kecuali dari membagikan intisari ini, sebutkan bagaimana Anda sampai pada hasil yang tha's exactly equivalent totidak dimiliki oleh frasa . Terima kasih sebelumnya. Akan menyenangkan untuk menjelaskan dengan mengomentari kode Anda. Terima kasih :)
Christos
8
Perlu juga disebutkan bahwa Task.Run membuka tugas bersarang secara default. Saya merekomendasikan untuk membaca artikel ini tentang perbedaan utama: blogs.msdn.microsoft.com/pfxteam/2011/10/24/…
Pawel Maga
1
@ The0bserver nggak, itu TaskScheduler.Default. Silakan lihat di sini Referenceource.microsoft.com/#mscorlib/system/threading/Tasks/… .
Christos
46

Orang sudah menyebutkan itu

Task.Run(A);

Setara dengan

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

Tapi tidak ada yang menyebutkan itu

Task.Factory.StartNew(A);

Setara dengan:

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

Seperti yang Anda lihat, dua parameter berbeda untuk Task.Rundan Task.Factory.StartNew:

  1. TaskCreationOptions- Task.RunPenggunaan TaskCreationOptions.DenyChildAttachyang berarti bahwa tugas anak-anak tidak dapat dilampirkan ke orang tua, pertimbangkan ini:

    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.");

    Ketika kita memohon parentTask.Wait(), childTasktidak akan ditunggu, meskipun kita ditentukan TaskCreationOptions.AttachedToParentuntuk itu, ini karena TaskCreationOptions.DenyChildAttachmelarang anak untuk melampirkan padanya. Jika Anda menjalankan kode yang sama dengan Task.Factory.StartNewalih - alih Task.Run, parentTask.Wait()akan menunggu childTaskkarena Task.Factory.StartNewdigunakanTaskCreationOptions.None

  2. TaskScheduler- Task.RunMenggunakan TaskScheduler.Defaultyang berarti bahwa penjadwal tugas default (yang menjalankan tugas di Thread Pool) akan selalu digunakan untuk menjalankan tugas. Task.Factory.StartNewdi sisi lain menggunakan TaskScheduler.Currentyang berarti penjadwal utas saat ini, mungkin TaskScheduler.Defaulttapi tidak selalu. Bahkan ketika mengembangkan Winformsatau WPFaplikasi itu diperlukan untuk memperbarui UI dari utas saat ini, untuk melakukan ini orang menggunakan TaskScheduler.FromCurrentSynchronizationContext()penjadwal tugas, jika Anda tidak sengaja membuat tugas lain yang berjalan di dalam tugas yang digunakan TaskScheduler.FromCurrentSynchronizationContext()penjadwal UI akan beku. Penjelasan lebih rinci tentang ini dapat ditemukan di sini

Jadi secara umum jika Anda tidak menggunakan tugas anak bersarang dan selalu ingin tugas Anda dieksekusi di Thread Pool lebih baik digunakan Task.Run, kecuali jika Anda memiliki beberapa skenario yang lebih kompleks.

Mykhailo Seniutovych
sumber
1
Ini adalah tip yang fantastis, harus menjadi jawaban yang diterima
Ali Bayat
30

Lihat artikel blog ini yang menjelaskan perbedaannya. Pada dasarnya melakukan:

Task.Run(A)

Sama seperti melakukan:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);   
Scott Chamberlain
sumber
28

The Task.Runpunya diperkenalkan dalam versi NET framework yang lebih baru dan itu dianjurkan .

Dimulai dengan .NET Framework 4.5, metode Task.Run adalah cara yang disarankan untuk meluncurkan tugas terikat-komputasi. Gunakan metode StartNew hanya ketika Anda membutuhkan kontrol berbutir halus untuk tugas yang sudah berjalan lama, terikat dengan komputasi.

The Task.Factory.StartNewmemiliki lebih banyak pilihan, Task.Runadalah singkatan a:

Metode Jalankan menyediakan serangkaian kelebihan yang memudahkan untuk memulai tugas dengan menggunakan nilai default. Ini adalah alternatif yang ringan untuk kelebihan StartNew.

Dan dengan singkatan Maksudku teknis pintas :

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

Menurut posting ini oleh Stephen Cleary, Task.Factory.StartNew () berbahaya:

Saya melihat banyak kode di blog dan di SO pertanyaan yang menggunakan Task.Factory.StartNew untuk memulai pekerjaan di utas latar belakang. Stephen Toub memiliki artikel blog yang luar biasa yang menjelaskan mengapa Task.Run lebih baik daripada Task.Factory.StartNew, tapi saya pikir banyak orang yang belum membacanya (atau tidak memahaminya). Jadi, saya telah mengambil argumen yang sama, menambahkan beberapa bahasa yang lebih kuat, dan kita akan lihat bagaimana kelanjutannya. :) StartNew memang menawarkan lebih banyak opsi daripada Task.Run, tetapi ini cukup berbahaya, seperti yang akan kita lihat. Anda harus memilih Task.Run daripada Task.Factory.StartNew dalam kode async.

Berikut alasan sebenarnya:

  1. Tidak mengerti delegasi async. Ini sebenarnya sama dengan poin 1 dalam alasan mengapa Anda ingin menggunakan StartNew. Masalahnya adalah ketika Anda mengirimkan delegasi async ke StartNew, wajar untuk mengasumsikan bahwa tugas yang dikembalikan mewakili delegasi itu. Namun, karena StartNew tidak mengerti delegasi async, tugas apa yang sebenarnya diwakili hanyalah awal dari delegasi itu. Ini adalah salah satu perangkap pertama yang ditemui coders ketika menggunakan StartNew dalam kode async.
  2. Penjadwal default yang membingungkan. OK, trik waktu pertanyaan: pada kode di bawah ini, utas apa yang dijalankan oleh metode “A”?
Task.Factory.StartNew(A);

private static void A() { }

Nah, Anda tahu itu pertanyaan jebakan, eh? Jika Anda menjawab "utas utas", saya minta maaf, tapi itu tidak benar. "A" akan berjalan pada TaskScheduler apa pun yang sedang dijalankan!

Jadi itu berarti bisa berpotensi berjalan di utas UI jika operasi selesai dan ini marshal kembali ke utas UI karena kelanjutan sebagai Stephen Cleary menjelaskan lebih lengkap di posnya.

Dalam kasus saya, saya mencoba menjalankan tugas di latar belakang saat memuat datagrid untuk tampilan sementara juga menampilkan animasi yang sibuk. Animasi sibuk tidak ditampilkan saat menggunakan Task.Factory.StartNew()tetapi animasi ditampilkan dengan benar ketika saya beralih ke Task.Run().

Untuk detailnya, silakan lihat https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

pengguna8128167
sumber
1

Terlepas dari kesamaan yaitu Task.Run () menjadi singkatan untuk Task.Factory.StartNew (), ada perbedaan menit antara perilaku mereka dalam kasus sinkronisasi dan delegasi async.

Misalkan ada dua metode berikut:

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

public int GetInt()
{
    return 1;
}

Sekarang perhatikan kode berikut.

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

Di sini, sync1 dan sync2 bertipe Task <int>

Namun, ada perbedaan dalam hal metode async.

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

Dalam skenario ini, async1 bertipe Task <int>, namun async2 bertipe Task <Task <int>>

Shubham Sharma
sumber
Ya, karena Task.Runtelah built-in fungsionalitas Unwrapmetode membuka . Berikut adalah posting blog yang menjelaskan alasan di balik keputusan ini.
Theodor Zoulias
-8

Dalam aplikasi saya yang memanggil dua layanan, saya membandingkan Task.Run dan Task.Factory.StartNew . Saya menemukan bahwa dalam kasus saya keduanya bekerja dengan baik. Namun, yang kedua lebih cepat.

Devendra Rusia
sumber
Saya tidak tahu mengapa jawaban ini layak mendapat "10" suara turun, meskipun itu mungkin tidak benar atau membantu ...
Mayer Spitzer