Saya selalu mendapat kesan bahwa menggunakan ThreadPool untuk (katakanlah tidak kritis) tugas latar belakang berumur pendek dianggap sebagai praktik terbaik, bahkan di ASP.NET, tetapi kemudian saya menemukan artikel ini yang tampaknya menyarankan sebaliknya - Argumennya adalah bahwa Anda harus meninggalkan ThreadPool untuk menangani permintaan terkait ASP.NET.
Jadi, inilah cara saya melakukan tugas asinkron kecil sejauh ini:
ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))
Dan artikel tersebut menyarankan untuk membuat utas secara eksplisit, mirip dengan:
new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()
Metode pertama memiliki keuntungan karena dikelola dan dibatasi, tetapi ada potensi (jika artikelnya benar) bahwa tugas latar belakang kemudian bersaing untuk mendapatkan untaian dengan penangan permintaan ASP.NET. Metode kedua membebaskan ThreadPool, tetapi dengan biaya tidak dibatasi dan dengan demikian berpotensi menggunakan terlalu banyak sumber daya.
Jadi pertanyaan saya adalah, apakah saran dalam artikel tersebut benar?
Jika situs Anda mendapatkan begitu banyak lalu lintas sehingga ThreadPool Anda semakin penuh, maka apakah lebih baik untuk keluar dari band, atau apakah ThreadPool penuh menyiratkan bahwa Anda sampai pada batas sumber daya Anda, dalam hal ini Anda seharusnya tidak mencoba memulai utas Anda sendiri?
Klarifikasi: Saya hanya bertanya dalam lingkup tugas asinkron kecil non-kritis (misalnya, pencatatan jarak jauh), bukan item pekerjaan mahal yang memerlukan proses terpisah (dalam kasus ini saya setuju Anda memerlukan solusi yang lebih kuat).
sumber
Jawaban:
Jawaban lain di sini tampaknya mengabaikan poin terpenting:
Kecuali Anda mencoba memparalelkan operasi intensif CPU untuk menyelesaikannya lebih cepat di situs dengan beban rendah, tidak ada gunanya menggunakan thread pekerja sama sekali.
Itu berlaku untuk utas gratis, yang dibuat oleh
new Thread(...)
, dan utas pekerja diThreadPool
yang meresponsQueueUserWorkItem
permintaan.Ya, itu benar, Anda dapat membuat
ThreadPool
proses ASP.NET kelaparan dengan mengantri terlalu banyak item pekerjaan. Ini akan mencegah ASP.NET memproses permintaan lebih lanjut. Informasi dalam artikel tersebut akurat dalam hal itu; kumpulan utas yang sama digunakan untukQueueUserWorkItem
juga digunakan untuk melayani permintaan.Tetapi jika Anda benar-benar mengantri cukup banyak item pekerjaan untuk menyebabkan kelaparan ini, maka Anda harus membuat kumpulan utas kelaparan! Jika Anda menjalankan ratusan operasi intensif CPU pada saat yang sama, apa gunanya memiliki thread pekerja lain untuk melayani permintaan ASP.NET, ketika mesin sudah kelebihan beban? Jika Anda mengalami situasi ini, Anda perlu mendesain ulang sepenuhnya!
Sebagian besar waktu saya melihat atau mendengar tentang kode multi-utas yang digunakan secara tidak tepat di ASP.NET, ini bukan untuk mengantri pekerjaan intensif CPU. Ini untuk mengantri pekerjaan terikat I / O. Dan jika Anda ingin melakukan pekerjaan I / O, maka Anda harus menggunakan thread I / O (I / O Completion Port).
Secara khusus, Anda harus menggunakan async callback yang didukung oleh kelas library apa pun yang Anda gunakan. Metode ini selalu diberi label dengan sangat jelas; mereka mulai dengan kata-kata
Begin
danEnd
. Seperti dalamStream.BeginRead
,Socket.BeginConnect
,WebRequest.BeginGetResponse
, dan sebagainya.Metode ini menggunakan
ThreadPool
, tetapi mereka menggunakan IOCP, yang tidak mengganggu permintaan ASP.NET. Mereka adalah jenis benang ringan khusus yang dapat "dibangunkan" oleh sinyal interupsi dari sistem I / O. Dan dalam aplikasi ASP.NET, Anda biasanya memiliki satu utas I / O untuk setiap utas pekerja, sehingga setiap permintaan dapat memiliki satu operasi asinkron yang antri. Itu benar-benar ratusan operasi asinkron tanpa penurunan kinerja yang signifikan (dengan asumsi subsistem I / O dapat mengikutinya). Ini jauh lebih dari yang Anda butuhkan.Perlu diingat bahwa delegasi asinkron tidak berfungsi dengan cara ini - mereka akan menggunakan thread pekerja, sama seperti
ThreadPool.QueueUserWorkItem
. Hanya metode async bawaan kelas pustaka .NET Framework yang mampu melakukan ini. Anda dapat melakukannya sendiri, tetapi ini rumit dan sedikit berbahaya dan mungkin di luar cakupan diskusi ini.Jawaban terbaik untuk pertanyaan ini, menurut saya, adalah jangan gunakan
ThreadPool
atauThread
contoh latar belakang di ASP.NET . Ini sama sekali tidak seperti memutar utas dalam aplikasi Windows Forms, di mana Anda melakukannya untuk menjaga agar UI tetap responsif dan tidak peduli seberapa efisiennya itu. Di ASP.NET, perhatian Anda adalah throughput , dan semua konteks yang beralih pada semua utas pekerja tersebut benar-benar akan mematikan throughput Anda apakah Anda menggunakanThreadPool
atau tidak.Tolong, jika Anda menemukan diri Anda menulis kode threading di ASP.NET - pertimbangkan apakah itu dapat ditulis ulang atau tidak untuk menggunakan metode asinkron yang sudah ada sebelumnya, dan jika tidak bisa, maka pertimbangkan apakah Anda benar-benar membutuhkan kode tersebut atau tidak untuk berjalan di utas latar belakang. Dalam sebagian besar kasus, Anda mungkin akan menambahkan kompleksitas tanpa keuntungan bersih.
sumber
Action<T>
sebagai callback, misalnya. Jika yang Anda maksud adalah pilihan apakah akan menggunakan thread pekerja atau thread I / O terjadi di tingkat terendah, itu disengaja; hanya tingkat itu yang dapat memutuskan apakah perlu IOCP atau tidak.ThreadPool
yang membatasi Anda dengan cara ini, mungkin karena mereka tidak mempercayai pengembang untuk melakukannya dengan benar. Windows Thread Pool yang tidak dikelola memiliki API yang sangat mirip tetapi sebenarnya memungkinkan Anda untuk memilih jenis utas.Menurut Thomas Marquadt dari tim ASP.NET di Microsoft, aman untuk menggunakan ASP.NET ThreadPool (QueueUserWorkItem).
Dari artikel :
sumber
Situs web tidak boleh mengelilingi utas pemijahan.
Anda biasanya memindahkan fungsi ini ke Layanan Windows yang kemudian berkomunikasi dengannya (saya menggunakan MSMQ untuk berbicara dengan mereka).
- Edit
Saya menjelaskan implementasinya di sini: Pemrosesan Latar Belakang Berbasis Antrean di Aplikasi Web ASP.NET MVC
- Edit
Untuk memperluas mengapa ini lebih baik dari sekedar utas:
Menggunakan MSMQ, Anda dapat berkomunikasi ke server lain. Anda dapat menulis ke antrian di seluruh mesin, jadi jika Anda menentukan, karena alasan tertentu, bahwa tugas latar belakang Anda menggunakan terlalu banyak sumber daya server utama, Anda dapat menggesernya dengan mudah.
Ini juga memungkinkan Anda untuk memproses tugas apa pun yang Anda coba lakukan (mengirim email / apa pun).
sumber
Saya pasti berpikir bahwa praktik umum untuk pekerjaan asinkron cepat dan prioritas rendah di ASP.NET akan menggunakan kumpulan utas .NET, terutama untuk skenario lalu lintas tinggi karena Anda ingin sumber daya Anda dibatasi.
Selain itu, implementasi threading disembunyikan - jika Anda mulai menelurkan utas Anda sendiri, Anda harus mengelolanya dengan benar juga. Bukan mengatakan Anda tidak bisa melakukannya, tetapi mengapa menciptakan kembali roda itu?
Jika kinerja menjadi masalah, dan Anda dapat menetapkan bahwa kumpulan utas adalah faktor pembatas (dan bukan koneksi database, koneksi jaringan keluar, memori, waktu tunggu halaman, dll.) Maka Anda mengubah konfigurasi kumpulan utas untuk memungkinkan lebih banyak utas pekerja, permintaan antrian yang lebih tinggi , dll.
Jika Anda tidak memiliki masalah kinerja maka memilih untuk menelurkan utas baru untuk mengurangi perselisihan dengan antrian permintaan ASP.NET adalah pengoptimalan prematur klasik.
Idealnya Anda tidak perlu menggunakan utas terpisah untuk melakukan operasi pencatatan - cukup aktifkan utas asli untuk menyelesaikan operasi secepat mungkin, di mana MSMQ dan utas / proses konsumen yang terpisah masuk ke gambar. Saya setuju bahwa ini lebih berat dan lebih banyak pekerjaan untuk diterapkan, tetapi Anda benar-benar membutuhkan daya tahan di sini - volatilitas antrean bersama dalam memori akan cepat usang.
sumber
Anda harus menggunakan QueueUserWorkItem, dan menghindari membuat utas baru seperti Anda akan menghindari wabah. Untuk visual yang menjelaskan mengapa Anda tidak akan membuat ASP.NET kelaparan, karena menggunakan ThreadPool yang sama, bayangkan pemain sulap yang sangat terampil menggunakan dua tangan untuk menyimpan setengah lusin pin bowling, pedang, atau apa pun dalam penerbangan. Untuk gambaran mengapa membuat utas Anda sendiri itu buruk, bayangkan apa yang terjadi di Seattle pada jam-jam sibuk ketika jalur masuk yang banyak digunakan ke jalan raya memungkinkan kendaraan langsung memasuki lalu lintas alih-alih menggunakan lampu dan membatasi jumlah pintu masuk menjadi satu setiap beberapa detik . Terakhir, untuk penjelasan detailnya, silakan lihat tautan ini:
http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
Terima kasih, Thomas
sumber
Artikel itu tidak benar. ASP.NET memiliki kumpulan utasnya sendiri, utas pekerja terkelola, untuk melayani permintaan ASP.NET. Kumpulan ini biasanya terdiri dari beberapa ratus utas dan terpisah dari kumpulan ThreadPool, yang merupakan kelipatan prosesor yang lebih kecil.
Menggunakan ThreadPool di ASP.NET tidak akan mengganggu thread pekerja ASP.NET. Menggunakan ThreadPool baik-baik saja.
Ini juga dapat diterima untuk menyiapkan satu utas yang hanya untuk mencatat pesan dan menggunakan pola produsen / konsumen untuk meneruskan pesan log ke utas itu. Dalam hal ini, karena utas berumur panjang, Anda harus membuat satu utas baru untuk menjalankan logging.
Menggunakan utas baru untuk setiap pesan pasti berlebihan.
Alternatif lain, jika Anda hanya berbicara tentang logging, adalah menggunakan library seperti log4net. Ini menangani logging di utas terpisah dan menangani semua masalah konteks yang bisa muncul dalam skenario itu.
sumber
Saya akan mengatakan artikel itu salah. Jika Anda menjalankan toko .NET yang besar, Anda dapat dengan aman menggunakan kumpulan di beberapa aplikasi dan beberapa situs web (menggunakan kumpulan aplikasi terpisah), hanya berdasarkan satu pernyataan di dokumentasi ThreadPool :
sumber
System.Threading.ThreadPool
.Saya ditanyai pertanyaan serupa di tempat kerja minggu lalu dan saya akan memberikan jawaban yang sama. Mengapa Anda menggunakan aplikasi web multi threading per permintaan? Server web adalah sistem fantastis yang sangat dioptimalkan untuk menyediakan banyak permintaan secara tepat waktu (mis. Multi threading). Pikirkan apa yang terjadi jika Anda meminta hampir semua halaman di web.
Anda memberi contoh penebangan jarak jauh, tapi itu harus menjadi perhatian logger Anda. Proses asynchronous harus ada untuk menerima pesan secara tepat waktu. Sam bahkan menunjukkan bahwa logger Anda (log4net) seharusnya sudah mendukung ini.
Sam juga benar karena menggunakan Thread Pool di CLR tidak akan menyebabkan masalah dengan kumpulan thread di IIS. Hal yang harus diperhatikan di sini adalah bahwa Anda tidak memijah utas dari suatu proses, Anda menelurkan utas baru dari utas threadpool IIS. Ada perbedaan dan perbedaan itu penting.
Sumber
sumber
Saya tidak setuju dengan artikel yang dirujuk (C # feeds.com). Sangat mudah untuk membuat utas baru tetapi berbahaya. Jumlah optimal utas aktif untuk dijalankan pada satu inti sebenarnya sangat rendah - kurang dari 10. Terlalu mudah untuk menyebabkan mesin membuang waktu untuk mengganti utas jika utas dibuat untuk tugas-tugas kecil. Utas adalah sumber daya yang MEMBUTUHKAN manajemen. Abstraksi WorkItem ada untuk menangani ini.
Ada pertukaran di sini antara mengurangi jumlah utas yang tersedia untuk permintaan dan membuat terlalu banyak utas untuk memungkinkan salah satu dari mereka diproses secara efisien. Ini adalah situasi yang sangat dinamis tetapi saya pikir salah satu yang harus dikelola secara aktif (dalam hal ini oleh kumpulan utas) daripada menyerahkannya kepada pemroses untuk tetap menjadi yang terdepan dalam pembuatan utas.
Akhirnya artikel tersebut membuat beberapa pernyataan yang cukup luas tentang bahaya menggunakan ThreadPool tetapi benar-benar membutuhkan sesuatu yang konkret untuk mendukungnya.
sumber
Apakah IIS menggunakan ThreadPool yang sama untuk menangani permintaan masuk tampaknya sulit untuk mendapatkan jawaban pasti, dan juga tampaknya telah berubah seiring versi. Jadi sepertinya ide yang bagus untuk tidak menggunakan thread ThreadPool secara berlebihan, sehingga IIS memiliki banyak dari mereka yang tersedia. Di sisi lain, memunculkan utas Anda sendiri untuk setiap tugas kecil sepertinya ide yang buruk. Agaknya, Anda memiliki semacam penguncian di logging Anda, jadi hanya satu utas yang dapat maju pada satu waktu, dan sisanya hanya akan bergiliran menjadwalkan dan tidak terjadwal (belum lagi overhead pemijahan utas baru). Pada dasarnya, Anda mengalami masalah yang sebenarnya dirancang untuk dihindari ThreadPool.
Tampaknya kompromi yang masuk akal adalah jika aplikasi Anda mengalokasikan satu utas logging yang dapat Anda kirimi pesan. Anda ingin berhati-hati bahwa pengiriman pesan secepat mungkin sehingga Anda tidak memperlambat aplikasi Anda.
sumber
Anda dapat menggunakan Parallel.For atau Parallel.ForEach dan menentukan batas kemungkinan utas yang ingin Anda alokasikan agar berjalan lancar dan mencegah kelaparan kumpulan.
Namun, dijalankan di latar belakang Anda perlu menggunakan gaya TPL murni di bawah ini dalam aplikasi web ASP.Net.
sumber