Lakukan operasi asinkron dalam ASP.NET MVC menggunakan utas dari ThreadPool pada .NET 4

158

Setelah pertanyaan ini, saya merasa nyaman saat menggunakan operasi async di ASP.NET MVC. Jadi, saya menulis dua posting blog tentang itu:

Saya memiliki terlalu banyak kesalahpahaman dalam pikiran saya tentang operasi asinkron di ASP.NET MVC.

Saya selalu mendengar kalimat ini: Aplikasi dapat meningkatkan skala jika operasi berjalan secara tidak sinkron

Dan saya sering mendengar kalimat semacam ini: jika Anda memiliki volume lalu lintas yang besar, Anda mungkin lebih baik tidak melakukan pertanyaan secara tidak sinkron - mengonsumsi 2 utas tambahan untuk melayani satu permintaan mengambil sumber daya dari permintaan masuk lainnya.

Saya pikir kedua kalimat itu tidak konsisten.

Saya tidak memiliki banyak informasi tentang cara kerja threadpool di ASP.NET tetapi saya tahu bahwa threadpool memiliki ukuran terbatas untuk utas. Jadi, kalimat kedua harus terkait dengan masalah ini.

Dan saya ingin tahu apakah operasi asinkron dalam ASP.NET MVC menggunakan utas dari ThreadPool di .NET 4?

Misalnya, ketika kita menerapkan AsyncController, bagaimana struktur aplikasi? Jika saya mendapatkan traffic yang besar, apakah itu ide yang baik untuk mengimplementasikan AsyncController?

Apakah ada orang di luar sana yang dapat mengambil tirai hitam ini di depan mata saya dan menjelaskan kepada saya kesepakatan tentang asinkron di ASP.NET MVC 3 (NET 4)?

Edit:

Saya telah membaca dokumen di bawah ini hampir ratusan kali dan saya mengerti masalah utamanya tetapi saya masih bingung karena terlalu banyak komentar yang tidak konsisten di luar sana.

Menggunakan Asynchronous Controller di ASP.NET MVC

Edit:

Mari kita asumsikan saya memiliki aksi controller seperti di bawah ini (bukan implementasi dari AsyncControllerolah):

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Seperti yang Anda lihat di sini, saya menjalankan operasi dan melupakannya. Kemudian, saya segera kembali tanpa menunggu itu selesai.

Dalam hal ini, apakah ini harus menggunakan utas dari threadpool? Jika demikian, setelah selesai, apa yang terjadi pada utas itu? Apakah GCmasuk dan membersihkan setelah selesai?

Edit:

Untuk jawaban @ Darwin, berikut adalah contoh kode async yang berbicara ke basis data:

public class FooController : AsyncController {

    //EF 4.2 DbContext instance
    MyContext _context = new MyContext();

    public void IndexAsync() { 

        AsyncManager.OutstandingOperations.Increment(3);

        Task<IEnumerable<Foo>>.Factory.StartNew(() => { 

           return 
                _context.Foos;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foos"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<Bars>>.Factory.StartNew(() => { 

           return 
                _context.Bars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["bars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<FooBar>>.Factory.StartNew(() => { 

           return 
                _context.FooBars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foobars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ViewResult IndexCompleted(
        IEnumerable<Foo> foos, 
        IEnumerable<Bar> bars,
        IEnumerable<FooBar> foobars) {

        //Do the regular stuff and return

    }
}
tugberk
sumber
Tidak yakin dengan jawabannya, tetapi perlu dicatat bahwa asinkron dan multi-threading adalah hal yang berbeda. Jadi mungkin untuk memiliki sejumlah utas dengan penanganan asinkron. Apa yang akan terjadi adalah ketika satu halaman harus diblokir untuk mengatakan, I / O, halaman lain akan mendapatkan kesempatan untuk berjalan di utas yang sama. Begitulah cara kedua pernyataan itu benar, async dapat membuat segalanya lebih cepat tetapi terlalu banyak utas menjadi masalah.
Chris Chilvers
@ ChrisChilvers Yap, multithreading tidak selalu diperlukan pada operasi asinkron. Saya sudah menemukan itu, tetapi saya pikir saya tidak memiliki pengendali sejauh yang saya tahu. AsyncController memutar berapa banyak utas yang diinginkan dari sudut pandang saya tetapi juga tidak yakin tentang itu. Apakah ada gagasan threadpool pada aplikasi desktop seperti WPF juga? Saya pikir jumlah utas bukan masalah pada aplikasi semacam itu.
tugberk
6
Lihatlah video Threading dengan Jeff Richter
oleksii
Saya pikir masalahnya (dan dengan demikian inkonsistensi) adalah pernyataan kedua menggunakan asinkron ketika artinya banyak utas. Ini bisa jadi karena itulah bagaimana asp.net telah mengimplementasikan halaman async dan dengan demikian implementasi spesifik telah membingungkan masalah ini (karena nama fitur yang menyebabkan masalah akan menjadi halaman async), tapi saya tidak yakin tentang implemntation spesifik . Jadi mereka berarti "banyak utas", atau yang mereka maksud adalah "halaman async dalam asp.net versi X" karena versi masa depan dapat mengubah implementasi. Atau mereka hanya bermaksud menggunakan kumpulan utas untuk melakukan async dalam satu halaman.
Chris Chilvers
@ChrisChilvers oh, teman! Saya lebih bingung setelah komentar ini: s
tugberk

Jawaban:

177

Berikut ini adalah artikel yang sangat baik saya sarankan Anda membaca untuk lebih memahami pemrosesan asynchronous di ASP.NET (yang mewakili apa yang pada dasarnya mewakili pengontrol asinkron).

Pertama mari kita pertimbangkan tindakan sinkron standar:

public ActionResult Index()
{
    // some processing
    return View();
}

Ketika permintaan dibuat untuk tindakan ini, sebuah thread diambil dari kumpulan thread dan tubuh dari tindakan ini dieksekusi pada thread ini. Jadi jika pemrosesan di dalam tindakan ini lambat, Anda memblokir utas ini untuk seluruh pemrosesan, jadi utas ini tidak dapat digunakan kembali untuk memproses permintaan lainnya. Di akhir eksekusi permintaan, utas dikembalikan ke utas kolam.

Sekarang mari kita ambil contoh pola asinkron:

public void IndexAsync()
{
    // perform some processing
}

public ActionResult IndexCompleted(object result)
{
    return View();
}

Ketika permintaan dikirim ke tindakan Indeks, utas diambil dari kumpulan utas dan tubuh IndexAsyncmetode dieksekusi. Setelah badan metode ini selesai dijalankan, utas dikembalikan ke kumpulan utas. Kemudian, menggunakan standar AsyncManager.OutstandingOperations, setelah Anda memberi sinyal penyelesaian operasi async, utas lain diambil dari kumpulan utas dan badan IndexCompletedtindakan dieksekusi di atasnya dan hasilnya diberikan kepada klien.

Jadi yang dapat kita lihat dalam pola ini adalah bahwa permintaan HTTP klien tunggal dapat dieksekusi oleh dua utas yang berbeda.

Sekarang bagian yang menarik terjadi di dalam IndexAsyncmetode. Jika Anda memiliki operasi pemblokiran di dalamnya, Anda benar-benar menyia-nyiakan seluruh tujuan pengontrol asinkron karena Anda memblokir utas pekerja (ingat bahwa tubuh tindakan ini dijalankan pada utas yang ditarik dari kumpulan utas).

Jadi kapan kita dapat mengambil manfaat nyata dari pengontrol asinkron yang mungkin Anda tanyakan?

IMHO, kita dapat memperoleh sebagian besar ketika kita memiliki operasi intensif I / O (seperti basis data dan panggilan jaringan ke layanan jarak jauh). Jika Anda memiliki operasi intensif CPU, tindakan asinkron tidak akan membawa banyak manfaat bagi Anda.

Jadi mengapa kita dapat memperoleh manfaat dari operasi intensif I / O? Karena kita bisa menggunakan I / O Penyelesaian Ports . IOCP sangat kuat karena Anda tidak menggunakan utas atau sumber daya apa pun di server selama pelaksanaan seluruh operasi.

Bagaimana mereka bekerja?

Misalkan kita ingin mengunduh konten halaman web jarak jauh menggunakan metode WebClient.DownloadStringAsync . Anda memanggil metode ini yang akan mendaftarkan IOCP dalam sistem operasi dan segera kembali. Selama pemrosesan seluruh permintaan, tidak ada utas yang digunakan di server Anda. Semuanya terjadi di server jarak jauh. Ini bisa memakan banyak waktu tetapi Anda tidak peduli karena Anda tidak membahayakan utas pekerja Anda. Setelah respons diterima, IOCP ditandai, utas diambil dari utas dan panggilan balik dieksekusi pada utas ini. Tapi seperti yang Anda lihat, selama seluruh proses, kami belum memonopoli utas apa pun.

Hal yang sama berlaku dengan metode seperti FileStream.BeginRead, SqlCommand.BeginExecute, ...

Bagaimana dengan memparalelkan beberapa panggilan basis data? Misalkan Anda memiliki tindakan pengontrol sinkron di mana Anda melakukan 4 pemblokiran panggilan basis data secara berurutan. Sangat mudah untuk menghitung bahwa jika setiap panggilan basis data membutuhkan 200 ms, tindakan pengontrol Anda akan membutuhkan sekitar 800 ms untuk dijalankan.

Jika Anda tidak perlu menjalankan panggilan itu secara berurutan, akankah memparalelasinya meningkatkan kinerja?

Itu pertanyaan besar, yang tidak mudah dijawab. Mungkin ya mungkin tidak. Ini sepenuhnya akan tergantung pada bagaimana Anda menerapkan panggilan database tersebut. Jika Anda menggunakan pengontrol async dan Port Penyelesaian I / O seperti yang dibahas sebelumnya, Anda akan meningkatkan kinerja tindakan pengontrol ini dan tindakan lainnya juga, karena Anda tidak akan memonopoli thread pekerja.

Di sisi lain, jika Anda menerapkannya dengan buruk (dengan panggilan basis data pemblokiran dilakukan pada utas dari utas), Anda pada dasarnya akan menurunkan total waktu pelaksanaan tindakan ini menjadi sekitar 200 ms, tetapi Anda akan menggunakan 4 utas pekerja sehingga Anda mungkin telah menurunkan kinerja permintaan lain yang mungkin menjadi kelaparan karena tidak ada utas untuk memprosesnya.

Jadi itu sangat sulit dan jika Anda tidak merasa siap untuk melakukan tes ekstensif pada aplikasi Anda, jangan menerapkan pengendali tidak sinkron, karena kemungkinan Anda akan melakukan lebih banyak kerusakan daripada manfaat. Terapkan hanya jika Anda memiliki alasan untuk melakukannya: misalnya Anda telah mengidentifikasi bahwa tindakan pengontrol sinkron standar adalah penghambat aplikasi Anda (setelah melakukan tes beban yang luas dan tentu saja pengukuran).

Sekarang mari kita perhatikan contoh Anda:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Ketika permintaan diterima untuk tindakan Indeks, sebuah thread diambil dari kumpulan thread untuk mengeksekusi tubuhnya, tetapi tubuhnya hanya menjadwalkan tugas baru menggunakan TPL . Jadi eksekusi tindakan berakhir dan utas dikembalikan ke utas kumpulan. Kecuali itu, TPL menggunakan utas dari kumpulan utas untuk melakukan pemrosesan. Jadi, bahkan jika utas asli dikembalikan ke kumpulan utas, Anda telah menarik utas lain dari kumpulan ini untuk menjalankan isi tugas. Jadi Anda telah membahayakan 2 utas dari kolam berharga Anda.

Sekarang mari kita pertimbangkan hal berikut:

public ViewResult Index() { 

    new Thread(() => { 
        //Do an advanced looging here which takes a while
    }).Start();

    return View();
}

Dalam hal ini kami secara manual memunculkan sebuah utas. Dalam hal ini, eksekusi tubuh tindakan Indeks mungkin memakan waktu sedikit lebih lama (karena memunculkan thread baru lebih mahal daripada menarik satu dari kumpulan yang ada). Tetapi pelaksanaan operasi logging lanjutan akan dilakukan pada utas yang bukan bagian dari kumpulan. Jadi kami tidak membahayakan utas dari kolam yang tetap gratis untuk melayani permintaan lain.

Darin Dimitrov
sumber
1
Sangat terperinci, terima kasih! Mari kita asumsikan, kita memiliki 4 async Tasks ( System.Threading.Task) yang menjalankan IndexAsyncmetode inside . Di dalam operasi itu, kami membuat panggilan db ke server. Jadi, semuanya adalah operasi intensif I / O, kan? Dalam hal itu, apakah kita membuat 4 utas terpisah (atau mendapat 4 utas terpisah dari kumpulan-utas)? Dengan asumsi saya memiliki mesin multi-core mereka akan berjalan secara paralel juga, bukan?
tugberk
10
@tugberk, panggilan basis data adalah operasi I / O, tetapi semuanya akan tergantung pada bagaimana Anda menerapkannya. Jika Anda menggunakan panggilan basis data pemblokiran seperti SqlCommand.ExecuteReaderAnda membuang-buang semuanya karena ini adalah panggilan pemblokiran. Anda memblokir utas yang digunakan panggilan ini dan jika utas ini merupakan utas dari kumpulan itu sangat buruk. Anda akan mendapatkan keuntungan hanya jika Anda menggunakan I / O Penyelesaian Ports: SqlCommand.BeginExecuteReader. Jika Anda tidak menggunakan IOCP apa pun yang Anda lakukan, jangan gunakan pengontrol async karena Anda akan melakukan lebih banyak kerusakan daripada manfaat pada kinerja keseluruhan aplikasi Anda.
Darin Dimitrov
1
Yah, sebagian besar waktu saya menggunakan kode EF terlebih dahulu. Saya tidak yakin apakah itu cocok. Saya memasang sampel yang menunjukkan apa yang biasanya saya lakukan. Saya memperbarui pertanyaan, dapatkah Anda melihatnya?
tugberk
3
@tugberk, Anda menjalankannya secara paralel, sehingga total waktu eksekusi kurang dibandingkan dengan jika Anda menjalankannya secara berurutan. Tetapi untuk menjalankannya, Anda menggunakan utas pekerja. Yah sebenarnya EF malas jadi ketika Anda melakukannya _context.FooAnda sebenarnya tidak mengeksekusi apa pun. Anda hanya membangun pohon ekspresi. Berhati-hatilah dengan ini. Eksekusi kueri ditangguhkan hanya ketika Anda mulai menghitung lebih dari hasil. Dan jika ini terjadi dalam tampilan ini mungkin menjadi bencana bagi kinerja. Untuk dengan bersemangat mengeksekusi menambahkan kueri EF .ToList()di akhir.
Darin Dimitrov
2
@tugberk, Anda akan memerlukan alat pengujian beban untuk mensimulasikan beberapa pengguna secara paralel di situs web Anda untuk melihat bagaimana berperilaku di bawah beban berat. Mini Profiler tidak dapat mensimulasikan pemuatan di situs web Anda. Mungkin membantu Anda melihat dan mengoptimalkan permintaan dan profil ADO.NET Anda satu permintaan yang tidak berguna ketika Anda perlu melihat bagaimana situs Anda berperilaku dalam situasi dunia nyata ketika banyak pengguna memukulnya.
Darin Dimitrov
49

Ya - semua utas berasal dari kumpulan utas. Aplikasi MVC Anda sudah multi-utas, saat permintaan masuk utas baru akan diambil dari kumpulan dan digunakan untuk melayani permintaan. Utas itu akan 'dikunci' (dari permintaan lain) sampai permintaan sepenuhnya dilayani dan diselesaikan. Jika tidak ada utas yang tersedia di kolam permintaan akan harus menunggu sampai tersedia.

Jika Anda memiliki pengontrol async, mereka masih mendapatkan utas dari kolam tetapi saat melayani permintaan mereka dapat menyerahkan utas, sambil menunggu sesuatu terjadi (dan utas itu dapat diberikan kepada permintaan lain) dan ketika permintaan awal membutuhkan utas lagi mendapat satu dari kolam renang.

Perbedaannya adalah bahwa jika Anda memiliki banyak permintaan jangka panjang (di mana utas menunggu tanggapan dari sesuatu), Anda mungkin kehabisan utas dari kolam untuk melayani bahkan permintaan dasar. Jika Anda memiliki pengontrol async, Anda tidak memiliki utas lagi tetapi utas yang menunggu dikembalikan ke kumpulan dan dapat melayani permintaan lainnya.

Sebuah contoh kehidupan yang hampir nyata ... Pikirkan itu seperti naik bus, ada lima orang menunggu untuk naik, yang pertama naik, membayar dan duduk (pengemudi dilayani permintaan mereka), Anda naik (pengemudi sedang melayani permintaan Anda) tetapi Anda tidak dapat menemukan uang Anda; ketika Anda meraba-raba di saku Anda, pengemudi menyerah pada Anda dan mendapatkan dua orang berikutnya (melayani permintaan mereka), ketika Anda menemukan uang Anda pengemudi mulai berurusan dengan Anda lagi (menyelesaikan permintaan Anda) - orang kelima harus menunggu sampai Anda sudah selesai tetapi orang ketiga dan keempat dilayani sementara Anda setengah jalan dilayani. Ini berarti bahwa pengemudi adalah satu-satunya utas dari kolam dan penumpang adalah permintaan. Terlalu rumit untuk menulis bagaimana cara kerjanya jika ada dua driver tetapi Anda dapat membayangkan ...

Tanpa pengontrol async, penumpang di belakang Anda harus menunggu lama saat Anda mencari uang Anda, sementara itu sopir bus tidak akan bekerja.

Jadi kesimpulannya adalah, jika banyak orang tidak tahu di mana uang mereka berada (yaitu memerlukan waktu lama untuk menanggapi sesuatu yang diminta sopir) pengontrol async dapat membantu throughput permintaan, mempercepat proses dari beberapa orang. Tanpa pengontrol aysnc, semua orang menunggu sampai orang di depan benar-benar ditangani. TAPI jangan lupa bahwa di MVC Anda memiliki banyak supir bis dalam satu bus sehingga async bukanlah pilihan otomatis.

K. Bob
sumber
8
Analogi yang sangat bagus. Terima kasih.
Pittsburgh DBA
Saya menyukai deskripsi. Terima kasih
Omer Cansizoglu
Cara terbaik untuk menjelaskannya. Terima kasih,
Anand Vyas
Jawaban Anda dalam kombinasi jawaban Darwin merangkum seluruh mekanisme di belakang pengontrol async, apa itu, dan yang lebih penting apa yang bukan!
Nirman
Ini adalah analogi yang bagus, tetapi satu-satunya pertanyaan saya adalah ini: pria yang meraba-raba dalam kantongnya dalam analogi Anda akan menjadi semacam pekerjaan / pemrosesan dalam aplikasi kita ... jadi proses apa yang bekerja setelah kita melepaskan utasnya? Tentunya itu adalah utas lainnya? Jadi apa yang kita dapatkan di sini?
Tomuke
10

Ada dua konsep yang dimainkan di sini. Pertama-tama kita dapat membuat kode kita berjalan secara paralel untuk mengeksekusi lebih cepat atau menjadwalkan kode pada utas lain untuk menghindari membuat pengguna menunggu. Contoh yang Anda miliki

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

termasuk dalam kategori kedua. Pengguna akan mendapatkan respons yang lebih cepat tetapi beban kerja total pada server lebih tinggi karena harus melakukan pekerjaan yang sama + menangani threading.

Contoh lain dari ini adalah:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Make async web request to twitter with WebClient.DownloadString()
    });

    Task.Factory.StartNew(() => { 
        //Make async web request to facebook with WebClient.DownloadString()
    });


    //wait for both to be ready and merge the results

    return View();
}

Karena permintaan berjalan secara paralel, pengguna tidak perlu menunggu selama jika dilakukan secara serial. Tetapi Anda harus menyadari bahwa kami menggunakan lebih banyak sumber daya di sini daripada jika kami menjalankan serial karena kami menjalankan kode di banyak utas sementara kami juga menunggu di atas utas.

Ini sangat baik dalam skenario klien. Dan sangat umum di sana untuk membungkus kode lama berjalan sinkron dalam tugas baru (jalankan di utas lain) juga menjaga ui responsif atau parallize untuk membuatnya lebih cepat. Sebuah utas masih digunakan untuk seluruh durasi. Pada server dengan beban tinggi ini bisa menjadi bumerang karena Anda benar-benar menggunakan lebih banyak sumber daya. Inilah yang diperingatkan orang tentang Anda

Pengontrol Async di MVC memiliki tujuan lain. Intinya di sini adalah untuk menghindari memiliki sittings benang di sekitar melakukan apa-apa (yang dapat merusak skalabilitas). Ini benar-benar hanya masalah jika API yang Anda panggil memiliki metode async. Seperti WebClient.DowloadStringAsync ().

Intinya adalah Anda dapat membiarkan utas Anda dikembalikan untuk menangani permintaan baru sampai permintaan web selesai di mana ia akan memanggil Anda callback yang mendapat utas yang sama atau baru dan menyelesaikan permintaan.

Saya harap Anda memahami perbedaan antara asinkron dan paralel. Pikirkan kode paralel sebagai kode tempat utas Anda berada dan tunggu hasilnya. Sementara kode asinkron adalah kode tempat Anda akan diberi tahu ketika kode selesai dan Anda dapat kembali bekerja, sementara itu utas dapat melakukan pekerjaan lain.

Mikael Eliasson
sumber
6

Aplikasi dapat meningkatkan skala jika operasi berjalan secara tidak sinkron, tetapi hanya jika ada sumber daya yang tersedia untuk melayani operasi tambahan .

Operasi asinkron memastikan bahwa Anda tidak pernah memblokir tindakan karena tindakan yang ada sedang berlangsung. ASP.NET memiliki model asinkron yang memungkinkan banyak permintaan untuk dieksekusi berdampingan. Mungkin saja untuk mengantri permintaan dan memprosesnya FIFO, tetapi ini tidak akan skala dengan baik ketika Anda memiliki ratusan permintaan yang antri dan setiap permintaan membutuhkan 100 ms untuk diproses.

Jika Anda memiliki volume lalu lintas yang sangat besar, Anda mungkin lebih baik tidak melakukan permintaan secara serempak, karena mungkin tidak ada sumber daya tambahan untuk melayani permintaan . Jika tidak ada sumber daya cadangan, permintaan Anda dipaksa untuk mengantri, memakan waktu lebih lama secara eksponensial atau gagal total, dalam hal ini overhead tidak sinkron (operasi mutex dan pengalihan konteks) tidak memberi Anda apa pun.

Sejauh ASP.NET berjalan, Anda tidak punya pilihan - itu menggunakan model asinkron, karena itulah yang masuk akal untuk model server-klien. Jika Anda harus menulis kode Anda sendiri secara internal yang menggunakan pola async untuk mencoba meningkatkan skala, kecuali Anda mencoba mengelola sumber daya yang dibagikan di antara semua permintaan, Anda tidak akan benar-benar melihat peningkatan karena sudah dibungkus dalam proses asinkron yang tidak memblokir hal lain.

Pada akhirnya, itu semua subjektif sampai Anda benar-benar melihat apa yang menyebabkan kemacetan dalam sistem Anda. Terkadang jelas bahwa pola asinkron akan membantu (dengan mencegah pemblokiran sumber daya yang antri). Pada akhirnya hanya mengukur dan menganalisis suatu sistem yang dapat menunjukkan di mana Anda dapat memperoleh efisiensi.

Edit:

Dalam contoh Anda, the Task.Factory.StartNew panggilan akan mengantri operasi di .NET thread-pool. Sifat utas Thread Pool harus digunakan kembali (untuk menghindari biaya membuat / menghancurkan banyak utas). Setelah operasi selesai, utas dilepaskan kembali ke kolam untuk digunakan kembali oleh permintaan lain (Pengumpul Sampah tidak benar-benar terlibat kecuali Anda membuat beberapa objek dalam operasi Anda, dalam hal ini mereka dikumpulkan sesuai normal pelingkupan).

Sejauh menyangkut ASP.NET, tidak ada operasi khusus di sini. Permintaan ASP.NET selesai tanpa menghormati tugas asinkron. Satu-satunya kekhawatiran adalah jika kumpulan utas Anda jenuh (yaitu tidak ada utas yang tersedia untuk melayani permintaan saat ini dan pengaturan kumpulan tidak memungkinkan lebih banyak utas dibuat), dalam hal ini permintaan diblokir menunggu untuk memulai tugas sampai utas pool menjadi tersedia.

Paul Turner
sumber
Terima kasih! Setelah saya membaca jawaban Anda, saya mengedit pertanyaan dengan contoh kode. Bisakah Anda melihatnya?
tugberk
Anda memiliki kalimat ajaib di sana untuk saya: Task.Factory.StartNewpanggilan akan mengantri operasi di kolam thread .NET. . Dalam konteks ini, yang mana yang benar di sini: 1-) Ini membuat utas baru dan setelah selesai, utas itu kembali ke utas dan menunggu di sana untuk digunakan kembali. 2-) Ia mendapatkan utas dari threadpool dan utas itu kembali ke threadpool dan menunggu di sana untuk digunakan kembali. 3-) Dibutuhkan pendekatan yang paling efisien dan dapat melakukan keduanya.
tugberk
1
Thread-pool membuat utas sesuai kebutuhan dan mendaur ulang utas saat tidak digunakan. Perilaku yang tepat bervariasi di berbagai versi CLR. Anda dapat menemukan informasi spesifik tentang hal ini di sini msdn.microsoft.com/en-us/library/0ka9477y.aspx
Paul Turner
Itu mulai terbentuk di pikiran saya sekarang. Jadi, CLR memiliki thread-pool, kan? Sebagai contoh, aplikasi WPF juga memiliki gagasan tentang thread-pool dan itu berkaitan dengan pool-sana juga.
tugberk
1
Thread Pool adalah hal tersendiri dalam CLR. Komponen lain yang "sadar" tentang kolam menunjukkan bahwa mereka menggunakan benang Pool Thread (jika perlu) daripada membuat dan menghancurkan yang mereka miliki. Membuat atau menghancurkan utas adalah operasi yang relatif mahal, sehingga penggunaan kumpulan adalah keuntungan efisiensi besar pada operasi jangka pendek.
Paul Turner
2

Ya, mereka menggunakan utas dari kumpulan utas. Sebenarnya ada panduan yang sangat bagus dari MSDN yang akan menangani semua pertanyaan Anda dan banyak lagi. Saya telah menemukan itu sangat berguna di masa lalu. Saksikan berikut ini!

http://msdn.microsoft.com/en-us/library/ee728598.aspx

Sementara itu, komentar + saran yang Anda dengar tentang kode asinkron harus diambil dengan sebutir garam. Sebagai permulaan, hanya membuat sesuatu yang async tidak selalu menjadikannya skala yang lebih baik, dan dalam beberapa kasus dapat membuat skala aplikasi Anda lebih buruk. Komentar lain yang Anda poskan tentang "volume lalu lintas yang sangat besar ..." juga hanya benar dalam konteks tertentu. Itu benar-benar tergantung pada apa operasi Anda lakukan, dan bagaimana mereka berinteraksi dengan bagian lain dari sistem.

Singkatnya, banyak orang memiliki banyak pendapat tentang async, tetapi mereka mungkin tidak benar di luar konteks. Saya akan mengatakan fokus pada masalah Anda yang sebenarnya, dan lakukan pengujian kinerja dasar untuk melihat apa yang dikontrol async, dll. Yang sebenarnya menangani aplikasi Anda .

AR
sumber
Saya sudah membaca dokumen itu mungkin ratusan kali dan masih banyak kebingungan (mungkin masalahnya adalah saya, siapa tahu). Ketika Anda melihat-lihat, Anda melihat begitu banyak komentar yang tidak konsisten tentang asynchrony di ASP.NET MVC seperti yang Anda lihat pada pertanyaan saya.
tugberk
untuk kalimat terakhir: di dalam action controller, saya meminta database 5 kali secara terpisah (saya harus) dan itu semua membutuhkan sekitar 400 ms. Kemudian, saya mengimplementasikan AsyncController dan menjalankannya secara paralel. Waktu respons berkurang secara dramatis hingga kira-kira. 200 ms. Tetapi saya tidak tahu berapa banyak utas yang dibuatnya, apa yang terjadi pada utas-utas itu setelah saya selesai dengan itu, benar-benar GCdatang dan membersihkannya setelah saya selesai sehingga aplikasi saya tidak akan memiliki kebocoran memori, begitu seterusnya. Ada ide di bagian itu.
tugberk
lampirkan debugger dan cari tahu.
AR
0

Hal pertama yang bukan MVC tetapi IIS yang memelihara thread pool. Jadi setiap permintaan yang datang ke aplikasi MVC atau ASP.NET dilayani dari utas yang dikelola dalam kumpulan utas. Hanya dengan membuat aplikasi Asynch ia menjalankan tindakan ini di utas yang berbeda dan segera melepaskan utas sehingga permintaan lain dapat diambil.

Saya telah menjelaskan hal yang sama dengan video detail ( http://www.youtube.com/watch?v=wvg13n5V0V0/ "MVC Asynch controllers dan starvation thread") yang menunjukkan bagaimana kelaparan thread terjadi di MVC dan bagaimana meminimalkannya dengan menggunakan MVC Asynch controllers. Saya juga telah mengukur antrian permintaan menggunakan perfmon sehingga Anda dapat melihat bagaimana antrian permintaan diturunkan untuk MVC asynch dan bagaimana terburuknya untuk operasi Synch.

Shivprasad Koirala
sumber