Adakah yang punya sumber daya yang bagus untuk menerapkan strategi kumpulan objek bersama untuk sumber daya terbatas sesuai dengan kumpulan koneksi Sql? (Yaitu akan dilaksanakan sepenuhnya bahwa itu aman thread).
Untuk menindaklanjuti berkenaan dengan permintaan @Aaronaught untuk klarifikasi, penggunaan kumpulan adalah untuk permintaan penyeimbangan beban ke layanan eksternal. Untuk memasukkannya ke dalam skenario yang mungkin akan lebih mudah untuk segera dipahami sebagai lawan dari situasi langsung saya. Saya memiliki objek sesi yang fungsinya mirip dengan ISession
objek dari NHibernate. Bahwa setiap sesi unik mengelola koneksi ke database. Saat ini saya memiliki 1 objek sesi berjalan lama dan saya menghadapi masalah di mana penyedia layanan saya membatasi penggunaan saya pada sesi individual ini.
Karena kurangnya harapan mereka bahwa satu sesi akan diperlakukan sebagai akun layanan yang berjalan lama, mereka tampaknya memperlakukannya sebagai klien yang memalu layanan mereka. Yang membawa saya ke pertanyaan saya di sini, alih-alih memiliki 1 sesi individu saya akan membuat kumpulan sesi yang berbeda dan membagi permintaan ke layanan di beberapa sesi daripada bukannya membuat satu titik fokus seperti yang saya lakukan sebelumnya.
Semoga latar belakang itu menawarkan nilai tetapi untuk langsung menjawab beberapa pertanyaan Anda:
T: Apakah benda-benda mahal untuk dibuat?
A: Tidak ada objek yang merupakan kumpulan sumber daya terbatas
T: Apakah mereka akan diperoleh / dirilis sangat sering?
A: Ya, sekali lagi mereka dapat dianggap sebagai NHibernate ISessions di mana 1 biasanya diperoleh dan dirilis selama setiap permintaan satu halaman.
T: Apakah layanan first-first-first-serve yang sederhana atau apakah Anda memerlukan sesuatu yang lebih cerdas, yaitu yang akan mencegah kelaparan?
A: Distribusi tipe robin bulat sederhana sudah cukup, dengan kelaparan saya anggap Anda maksud jika tidak ada sesi yang tersedia sehingga penelepon diblokir menunggu rilis. Ini tidak benar-benar berlaku karena sesi dapat dibagikan oleh penelepon yang berbeda. Tujuan saya adalah mendistribusikan penggunaannya di beberapa sesi yang bertentangan dengan 1 sesi tunggal.
Saya percaya ini mungkin merupakan penyimpangan dari penggunaan normal dari kumpulan objek yang mengapa saya awalnya meninggalkan bagian ini dan merencanakan hanya untuk mengadaptasi pola untuk memungkinkan berbagi objek sebagai lawan memungkinkan situasi kelaparan yang pernah terjadi.
T: Bagaimana dengan hal-hal seperti prioritas, pemuatan malas vs bersemangat, dll.?
A: Tidak ada prioritisasi yang terlibat, demi kesederhanaan hanya berasumsi bahwa saya akan membuat kumpulan objek yang tersedia pada pembuatan pool itu sendiri.
sumber
Jawaban:
Penyatuan Objek dalam .NET Core
The dotnet inti memiliki implementasi objek pooling ditambahkan ke perpustakaan kelas dasar (BCL). Anda dapat membaca masalah GitHub asli di sini dan melihat kode untuk System.Buffers . Saat ini
ArrayPool
adalah satu-satunya jenis yang tersedia dan digunakan untuk mengumpulkan array. Ada posting blog yang bagus di sini .Contoh penggunaannya dapat dilihat di ASP.NET Core. Karena berada dalam BCL dotnet core, ASP.NET Core dapat berbagi kumpulan objek itu dengan objek lain seperti JSON serializer Newtonsoft.Json. Anda dapat membaca posting blog ini untuk informasi lebih lanjut tentang bagaimana Newtonsoft.Json melakukan ini.
Pooling Objek di Microsoft Roslyn C # Compiler
Kompiler Microsoft Roslyn C # yang baru berisi tipe ObjectPool , yang digunakan untuk menyatukan objek yang sering digunakan yang biasanya mendapatkan yang baru dan sampah dikumpulkan sangat sering. Ini mengurangi jumlah dan ukuran operasi pengumpulan sampah yang harus terjadi. Ada beberapa sub-implementasi yang berbeda menggunakan ObjectPool (Lihat: Mengapa ada begitu banyak implementasi Object Pooling di Roslyn? ).
1 - SharedPools - Menyimpan kumpulan 20 objek atau 100 jika BigDefault digunakan.
2 - ListPool dan StringBuilderPool - Tidak sepenuhnya memisahkan implementasi tetapi pembungkus di sekitar implementasi SharedPools yang ditunjukkan di atas khusus untuk List dan StringBuilder. Jadi ini menggunakan kembali kumpulan objek yang disimpan dalam SharedPools.
3 - PooledDictionary dan PooledHashSet - Ini menggunakan ObjectPool secara langsung dan memiliki kumpulan objek yang benar-benar terpisah. Menyimpan kumpulan 128 objek.
Microsoft.IO.RecyclableMemoryStream
Perpustakaan ini menyediakan kumpulan untuk
MemoryStream
objek. Ini pengganti pengganti untukSystem.IO.MemoryStream
. Memiliki semantik yang persis sama. Ini dirancang oleh insinyur Bing. Baca posting blog di sini atau lihat kode di GitHub .Catatan yang
RecyclableMemoryStreamManager
harus dideklarasikan sekali dan itu akan hidup untuk seluruh proses - ini adalah kolam. Sangatlah baik untuk menggunakan beberapa kolam jika Anda inginkan.sumber
RecyclableMemoryStream
itu adalah tambahan yang luar biasa untuk optimisasi kinerja sangat tinggi.Pertanyaan ini sedikit lebih rumit daripada yang diperkirakan karena beberapa hal yang tidak diketahui: Perilaku sumber daya yang dikumpulkan, umur objek yang diharapkan / diperlukan, alasan sebenarnya bahwa kolam diperlukan, dll. Biasanya kolam adalah tujuan khusus - utas pool, pool koneksi, dll. - karena lebih mudah untuk mengoptimalkannya ketika Anda tahu persis apa yang sumber daya lakukan dan yang lebih penting memiliki kontrol atas bagaimana sumber daya itu diimplementasikan.
Karena itu tidak sesederhana itu, apa yang saya coba lakukan adalah menawarkan pendekatan yang cukup fleksibel yang dapat Anda coba dan lihat apa yang paling berhasil. Permintaan maaf di muka untuk posting lama, tetapi ada banyak alasan untuk membahas penerapan sumber daya tujuan umum yang layak. dan aku benar-benar hanya menggaruk permukaan.
Kumpulan tujuan umum harus memiliki beberapa "pengaturan" utama, termasuk:
Untuk mekanisme pemuatan sumber daya, .NET sudah memberi kami abstraksi bersih - delegasi.
Lewati ini melalui konstruktor kolam dan kita hampir selesai dengan itu. Menggunakan tipe generik dengan a
new()
kendala juga berfungsi, tetapi ini lebih fleksibel.Dari dua parameter lainnya, strategi akses adalah binatang yang lebih rumit, jadi pendekatan saya adalah menggunakan pendekatan berbasis warisan (antarmuka):
Konsep di sini sederhana - kami akan membiarkan publik
Pool
kelas menangani masalah umum seperti keamanan utas, tetapi menggunakan "toko barang" yang berbeda untuk setiap pola akses. LIFO mudah diwakili oleh stack, FIFO adalah antrian, dan saya telah menggunakan implementasi buffer bundar yang tidak terlalu dioptimalkan tetapi menggunakanList<T>
pointer dan indeks pointer untuk memperkirakan pola akses round-robin.Semua kelas di bawah ini adalah kelas dalam dari
Pool<T>
- ini adalah pilihan gaya, tetapi karena ini benar-benar tidak dimaksudkan untuk digunakan di luarPool
, itu paling masuk akal.Ini adalah yang jelas - tumpukan dan antrian. Saya tidak berpikir mereka benar-benar membutuhkan banyak penjelasan. Buffer bundar sedikit lebih rumit:
Saya bisa saja memilih sejumlah pendekatan yang berbeda, tetapi intinya adalah bahwa sumber daya harus diakses dalam urutan yang sama dengan yang dibuat, yang berarti bahwa kita harus mempertahankan referensi kepada mereka tetapi menandainya sebagai "sedang digunakan" (atau tidak ). Dalam skenario terburuk, hanya satu slot yang pernah tersedia, dan dibutuhkan pengulangan buffer penuh untuk setiap pengambilan. Ini buruk jika Anda memiliki ratusan sumber daya yang dikumpulkan dan memperoleh dan melepaskannya beberapa kali per detik; tidak benar-benar masalah untuk kumpulan 5-10 item, dan di kasus khas , di mana sumber daya digunakan dengan ringan, hanya perlu memajukan satu atau dua slot.
Ingat, kelas-kelas ini adalah kelas batin pribadi - itulah sebabnya mereka tidak perlu banyak memeriksa kesalahan, kumpulan itu sendiri membatasi akses ke mereka.
Lemparkan ke dalam enumerasi dan metode pabrik dan kita selesai dengan bagian ini:
Masalah selanjutnya yang harus dipecahkan adalah strategi pemuatan. Saya telah mendefinisikan tiga jenis:
Dua yang pertama harus jelas; yang ketiga adalah semacam hibrida, itu memuat sumber daya tetapi tidak benar-benar mulai menggunakan kembali sumber daya apa pun sampai kolam penuh. Ini akan menjadi trade-off yang baik jika Anda ingin kumpulan menjadi penuh (yang sepertinya Anda lakukan) tetapi ingin menunda biaya untuk benar-benar membuatnya sampai akses pertama (yaitu untuk meningkatkan waktu startup).
Metode pemuatan benar-benar tidak terlalu rumit, karena sekarang kami memiliki abstraksi item-store:
Bidang
size
dancount
di atas merujuk pada ukuran maksimum kumpulan dan jumlah total sumber daya yang dimiliki oleh kumpulan ( masing-masing tidak harus tersedia ).AcquireEager
adalah yang paling sederhana, diasumsikan bahwa suatu barang sudah ada di toko - barang-barang ini akan dimuat pada saat konstruksi, yaitu diPreloadItems
metode yang ditunjukkan terakhir.AcquireLazy
memeriksa untuk melihat apakah ada item gratis di kolam renang, dan jika tidak, itu membuat yang baru.AcquireLazyExpanding
akan membuat sumber daya baru selama pool belum mencapai ukuran targetnya. Saya sudah mencoba mengoptimalkan ini untuk meminimalkan penguncian, dan saya harap saya tidak membuat kesalahan (saya punya menguji ini di bawah kondisi multi-threaded, tapi jelas tidak lengkap).Anda mungkin bertanya-tanya mengapa tidak ada metode ini yang repot-repot memeriksa untuk melihat apakah toko telah mencapai ukuran maksimum atau tidak. Saya akan membahasnya sebentar lagi.
Sekarang untuk kolam itu sendiri. Berikut ini adalah set lengkap data pribadi, beberapa di antaranya telah ditunjukkan:
Menjawab pertanyaan yang saya sampaikan pada paragraf terakhir - bagaimana memastikan kita membatasi jumlah total sumber daya yang dibuat - ternyata .NET sudah memiliki alat yang sangat bagus untuk itu, itu disebut Semaphore dan dirancang khusus untuk memungkinkan perbaikan jumlah utas akses ke sumber daya (dalam hal ini "sumber daya" adalah toko item dalam). Karena kita tidak menerapkan antrian produsen / konsumen sepenuhnya, ini sangat memadai untuk kebutuhan kita.
Konstruktornya terlihat seperti ini:
Seharusnya tidak ada kejutan di sini. Satu-satunya hal yang perlu diperhatikan adalah casing khusus untuk eager loading, menggunakan
PreloadItems
metode yang sudah ditunjukkan sebelumnya.Karena hampir semuanya telah diabstraksi secara bersih sekarang, metode
Acquire
dan aktualnyaRelease
sangat mudah:Seperti yang dijelaskan sebelumnya, kami menggunakan
Semaphore
untuk mengontrol konkurensi alih-alih memeriksa status toko item secara agama. Selama item yang diperoleh dirilis dengan benar, tidak ada yang perlu dikhawatirkan.Terakhir, ada pembersihan:
Tujuan dari
IsDisposed
properti itu akan menjadi jelas dalam sekejap. SemuaDispose
metode utama yang benar-benar dilakukan adalah membuang item yang dikumpulkan jika mereka menerapkanIDisposable
.Sekarang Anda pada dasarnya dapat menggunakan ini apa adanya, dengan sebuah
try-finally
blok, tapi saya tidak menyukai sintaks itu, karena jika Anda mulai membagikan sumber daya yang dikumpulkan antara kelas dan metode maka itu akan menjadi sangat membingungkan. Mungkin saja kelas utama yang menggunakan sumber daya tidak memilikinya referensi ke kumpulan. Ini benar-benar menjadi sangat berantakan, jadi pendekatan yang lebih baik adalah membuat objek yang dikumpulkan "pintar".Katakanlah kita mulai dengan antarmuka / kelas sederhana berikut:
Inilah
Foo
sumber daya pura-pura sekali pakai kami yang mengimplementasikanIFoo
dan memiliki beberapa kode boilerplate untuk menghasilkan identitas unik. Apa yang kami lakukan adalah membuat objek khusus yang dikumpulkan:Ini hanya proksi semua metode "nyata" ke dalamnya
IFoo
(kita bisa melakukan ini dengan perpustakaan Proxy Dinamis seperti Castle, tapi saya tidak akan membahasnya). Itu juga memelihara referensi kePool
yang membuatnya, sehingga ketika kitaDispose
objek ini, secara otomatis melepaskan dirinya kembali ke kolam. Kecuali ketika pool telah dibuang - ini berarti kita berada dalam mode "pembersihan" dan dalam hal ini sebenarnya membersihkan sumber daya internal sebagai gantinya.Dengan menggunakan pendekatan di atas, kita dapat menulis kode seperti ini:
Ini adalah hal yang sangat baik untuk dapat dilakukan. Ini berarti bahwa kode yang menggunakan satu
IFoo
(yang bertentangan dengan kode yang menciptakan itu) tidak benar-benar perlu menyadari dari kolam renang. Anda bahkan dapat menyuntikkanIFoo
objek menggunakan perpustakaan DI favorit Anda danPool<T>
sebagai penyedia / pabrik.Saya telah memasukkan kode lengkap pada PasteBin untuk kesenangan copy-and-paste Anda. Ada juga program pengujian singkat yang dapat Anda gunakan untuk bermain-main dengan berbagai mode pemuatan / akses dan kondisi multithreaded, untuk meyakinkan diri sendiri bahwa ini aman untuk benang dan tidak bermasalah.
Beri tahu saya jika Anda memiliki pertanyaan atau masalah tentang semua ini.
sumber
Sesuatu seperti ini mungkin sesuai dengan kebutuhan Anda.
Contoh Penggunaan
sumber
Put
metode dan meninggalkannya untuk kesederhanaan beberapa jenis pemeriksaan apakah objek tersebut rusak dan untuk membuat contoh baru yang akan ditambahkan ke kumpulan bukan memasukkan sebelumnya?Contoh dari MSDN: Cara: Membuat Pool Objek dengan Menggunakan ConcurrentBag
sumber
Kembali pada hari Microsoft menyediakan kerangka kerja melalui Microsoft Transaction Server (MTS) dan kemudian COM + untuk melakukan pengumpulan objek untuk objek COM. Fungsionalitas itu dibawa ke System.EnterpriseServices di .NET Framework dan sekarang di Windows Communication Foundation.
Pool Objek di WCF
Artikel ini dari. NET 1.1 tetapi harus tetap berlaku dalam versi Kerangka saat ini (meskipun WCF adalah metode yang disukai).
Objek Pooling .NET
sumber
IInstanceProvider
antarmuka ada karena saya akan menerapkan ini untuk solusi saya. Saya selalu penggemar menumpuk kode saya di belakang antarmuka yang disediakan Microsoft ketika mereka memberikan definisi yang pas.Saya sangat suka implementasi Aronaught - terutama karena ia menangani menunggu sumber daya menjadi tersedia melalui penggunaan semafor. Ada beberapa tambahan yang ingin saya buat:
sync.WaitOne()
kesync.WaitOne(timeout)
dan tampilkan batas waktu sebagai parameter padaAcquire(int timeout)
metode. Ini juga akan memerlukan penanganan kondisi ketika utas habis menunggu suatu objek tersedia.Recycle(T item)
metode untuk menangani situasi ketika suatu objek perlu didaur ulang ketika kegagalan terjadi, misalnya.sumber
Ini adalah implementasi lain, dengan jumlah objek terbatas.
sumber
Berorientasi Java, artikel ini mengungkap pola pool connectionImpl dan pola pool objek abstrak dan bisa menjadi pendekatan pertama yang baik: http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool. htm
Pola kolam objek:
sumber
Perpanjangan dari msdn tentang cara membuat kumpulan objek menggunakan ConcurrentBag.
https://github.com/chivandikwa/ObjectPool
sumber
Anda dapat menggunakan paket nuget
Microsoft.Extensions.ObjectPool
Dokumentasi di sini:
https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool?view=aspnetcore-3.1 https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.objectpool
sumber