ऑब्जेक्ट पूल पैटर्न
ऑब्जेक्ट पूल पैटर्न एक सॉफ़्टवेयर रचनात्मक डिज़ाइन पैटर्न है जो प्रारंभिक वस्तुओं के एक सेट का उपयोग करने के लिए तैयार रखा जाता है - एक "पूल" - मांग पर उन्हें आवंटित करने और नष्ट करने के अतिरिक्त। पूल का क्लाइंट पूल से किसी ऑब्जेक्ट का अनुरोध करेगा और लौटाए गए ऑब्जेक्ट पर ऑपरेशन करेगा। जब ग्राहक समाप्त हो जाता है, तो यह वस्तु को नष्ट करने के अतिरिक्त पूल में वापस कर देता है; यह मैन्युअल रूप से या स्वचालित रूप से किया जा सकता है।
ऑब्जेक्ट पूल मुख्य रूप से प्रदर्शन के लिए उपयोग किए जाते हैं: कुछ परिस्थितियों में, ऑब्जेक्ट पूल प्रदर्शन में काफी सुधार करते हैं। ऑब्जेक्ट पूल ऑब्जेक्ट के जीवनकाल को जटिल बनाते हैं, क्योंकि पूल से प्राप्त और लौटाए गए ऑब्जेक्ट वास्तव में इस समय बनाए या नष्ट नहीं किए जाते हैं, और इस प्रकार कार्यान्वयन में संरक्षण की आवश्यकता होती है।
विवरण
जब कई वस्तुओं के साथ कार्य करना आवश्यक होता है जो विशेष रूप से महंगे होते हैं और प्रत्येक वस्तु की आवश्यकता केवल थोड़े समय के लिए होती है, तो संपूर्ण अनुप्रयोग का प्रदर्शन प्रतिकूल रूप से प्रभावित हो सकता है। ऐसे स्थितियों में ऑब्जेक्ट पूल डिज़ाइन पैटर्न को वांछनीय माना जा सकता है।
ऑब्जेक्ट पूल डिज़ाइन पैटर्न उन वस्तुओं का एक सेट बनाता है जिनका पुन: उपयोग किया जा सकता है। जब किसी नई वस्तु की आवश्यकता होती है, तो उसे पूल से अनुरोध किया जाता है। यदि पहले से तैयार वस्तु उपलब्ध है, तो तात्कालिक लागत से बचते हुए, इसे तुरंत वापस कर दिया जाता है। यदि पूल में कोई वस्तु सम्मिलित नहीं है, तो एक नया आइटम बनाया जाता है और लौटाया जाता है। जब ऑब्जेक्ट का उपयोग किया गया है और अब इसकी आवश्यकता नहीं है, तो इसे पूल में वापस कर दिया जाता है, जिससे इसे भविष्य में कम्प्यूटेशनल रूप से महंगी तात्कालिकता प्रक्रिया को दोहराए बिना फिर से उपयोग करने की स्वीकृति मिलती है। यह ध्यान रखना महत्वपूर्ण है कि एक बार किसी वस्तु का उपयोग करने और लौटाने के बाद, सम्मिलिता संदर्भ अमान्य हो जाएंगे।
कुछ ऑब्जेक्ट पूल में संसाधन सीमित होते हैं, इसलिए ऑब्जेक्ट्स की अधिकतम संख्या निर्दिष्ट की जाती है। यदि यह संख्या पूरी हो जाती है और एक नए आइटम का अनुरोध किया जाता है, तो एक अपवाद फेंका जा सकता है, या थ्रेड को तब तक ब्लॉक किया जाएगा जब तक कि ऑब्जेक्ट पूल में वापस नहीं आ जाता।
ऑब्जेक्ट पूल डिज़ाइन पैटर्न का उपयोग .NET फ्रेमवर्क के मानक वर्गों में कई स्थानों पर किया जाता है। एक उदाहरण एसक्यूएल सर्वर के लिए .NET फ्रेमवर्क डेटा प्रदाता है। चूंकि एसक्यूएल सर्वर डेटाबेस कनेक्शन बनाने में धीमा हो सकता है, कनेक्शन का पूल बनाए रखा जाता है। कनेक्शन बंद करना वास्तव में एसक्यूएल सर्वर से लिंक को त्यागना नहीं है। इसके अतिरिक्त, कनेक्शन को पूल में रखा जाता है, जिससे नए कनेक्शन का अनुरोध करते समय इसे पुनर्प्राप्त किया जा सकता है। यह कनेक्शन बनाने की गति को काफी हद तक बढ़ा देता है।
लाभ
ऑब्जेक्ट पूलिंग उन स्थितियों में महत्वपूर्ण प्रदर्शन को बढ़ावा दे सकता है जहां एक क्लास इंस्टेंस को इनिशियलाइज़ करने की लागत अधिक है और एक क्लास के इन्स्टेन्शियशन और विनाश की दर उच्च है - इस स्थितिे में ऑब्जेक्ट्स का बार-बार पुन: उपयोग किया जा सकता है, और प्रत्येक पुन: उपयोग से महत्वपूर्ण मात्रा में बचत होती है। समय। ऑब्जेक्ट पूलिंग के लिए संसाधनों की आवश्यकता होती है - मेमोरी और संभवतः अन्य संसाधन, जैसे कि नेटवर्क सॉकेट, और इस प्रकार यह बेहतर है कि किसी एक समय में उपयोग किए जाने वाले उदाहरणों की संख्या कम हो, लेकिन इसकी आवश्यकता नहीं है।
पूल की गई वस्तु अनुमानित समय में प्राप्त होती है जब नई वस्तुओं (विशेष रूप से नेटवर्क पर) के निर्माण में परिवर्तनशील समय लग सकता है। ये लाभ अधिकतर उन वस्तुओं के लिए सही हैं जो समय के साथ महंगे हैं, जैसे डेटाबेस कनेक्शन, सॉकेट कनेक्शन, थ्रेड्स और बड़े ग्राफिक ऑब्जेक्ट्स जैसे फोंट या बिटमैप्स।
अन्य स्थितियों में, साधारण ऑब्जेक्ट पूलिंग (जिसमें कोई बाहरी संसाधन नहीं होता है, लेकिन केवल स्मृति पर कब्जा होता है) कुशल नहीं हो सकता है और प्रदर्शन को कम कर सकता है।[1] साधारण मेमोरी पूलिंग के स्थितिे में, स्लैब आवंटन मेमोरी प्रबंधन तकनीक अधिक अनुकूल है, क्योंकि एकमात्र लक्ष्य विखंडन को कम करके मेमोरी आवंटन और डीलोकेशन की लागत को कम करना है।
कार्यान्वयन
ऑब्जेक्ट पूल को स्मार्ट पॉइंटर्स के माध्यम से सी ++ जैसी भाषाओं में स्वचालित तरीके से कार्यान्वित किया जा सकता है। स्मार्ट पॉइंटर के कंस्ट्रक्टर में, ऑब्जेक्ट को पूल से रिक्वेस्ट किया जा सकता है, और स्मार्ट पॉइंटर के डिस्ट्रक्टर में, ऑब्जेक्ट को पूल में वापस छोड़ा जा सकता है। कचरा एकत्र करने वाली भाषाओं में, जहां कोई विध्वंसक नहीं है (जो स्टैक अनविंड के हिस्से के रूप में कहे जाने की गारंटी है), ऑब्जेक्ट पूल को मैन्युअल रूप से लागू किया जाना चाहिए, स्पष्ट रूप से फैक्टरी (ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग) से किसी वस्तु का अनुरोध करके और निपटान विधि को कॉल करके वस्तु को वापस करना। (डिस्पोज पैटर्न के अनुसार)। ऐसा करने के लिए फ़ाइनलाइज़र का उपयोग करना एक अच्छा विचार नहीं है, क्योंकि फ़ाइनलाइज़र कब (या यदि) चलाया जाएगा, इसकी कोई गारंटी नहीं है। इसके अतिरिक्त, "कोशिश करें ... अंत में" का उपयोग यह सुनिश्चित करने के लिए किया जाना चाहिए कि वस्तु प्राप्त करना और जारी करना अपवाद-तटस्थ है।
मैनुअल ऑब्जेक्ट पूल को लागू करना सरल है, लेकिन उपयोग करना कठिन है, क्योंकि उन्हें पूल ऑब्जेक्ट के मैन्युअल मेमोरी प्रबंधन की आवश्यकता होती है।
खाली पूलों का संचालन
ऑब्जेक्ट पूल अनुरोध को संभालने के लिए तीन रणनीतियों में से एक को नियोजित करता है जब पूल में कोई अतिरिक्त वस्तु नहीं होती है।
- ऑब्जेक्ट प्रदान करने में विफल (और क्लाइंट को एक त्रुटि लौटाएं)।
- एक नई वस्तु आवंटित करें, जिससे पूल का आकार बढ़ जाए। ऐसा करने वाले पूल आमतौर पर आपको उच्च जल चिह्न (कभी उपयोग की जाने वाली वस्तुओं की अधिकतम संख्या) सेट करने की स्वीकृति देते हैं।
- एक थ्रेड (कंप्यूटर साइंस) वातावरण में, एक पूल क्लाइंट को तब तक ब्लॉक कर सकता है जब तक कि कोई अन्य थ्रेड किसी ऑब्जेक्ट को पूल में वापस नहीं कर देता।
नुकसान
यह सुनिश्चित करने के लिए ध्यान रखा जाना चाहिए कि पूल में लौटाई गई वस्तुओं की स्थिति वस्तु के अगले उपयोग के लिए एक समझदार स्थिति में वापस आ जाए, अन्यथा वस्तु क्लाइंट द्वारा अप्रत्याशित स्थिति में हो सकती है, जिससे यह विफल हो सकती है। पूल ऑब्जेक्ट्स को रीसेट करने के लिए ज़िम्मेदार है, क्लाइंट नहीं। खतरनाक रूप से बासी अवस्था वाली वस्तुओं से भरे ऑब्जेक्ट पूल को कभी-कभी ऑब्जेक्ट सेसपूल कहा जाता है और इसे एक प्रतिमान के रूप में माना जाता है।
बासी स्थिति हमेशा एक मुद्दा नहीं हो सकती है; यह खतरनाक हो जाता है जब यह वस्तु को अप्रत्याशित रूप से व्यवहार करने का कारण बनता है। उदाहरण के लिए, प्रमाणीकरण विवरण का प्रतिनिधित्व करने वाली वस्तु विफल हो सकती है यदि सफलतापूर्वक प्रमाणित ध्वज को पुन: उपयोग करने से पहले रीसेट नहीं किया जाता है, क्योंकि यह इंगित करता है कि उपयोगकर्ता प्रमाणित है (संभवतः किसी और के रूप में) जब वे नहीं हैं। हालाँकि, केवल डिबगिंग के लिए उपयोग किए गए मान को रीसेट करने में विफल होने पर, जैसे कि उपयोग किए गए अंतिम प्रमाणीकरण सर्वर की पहचान, कोई समस्या नहीं हो सकती है।
वस्तुओं की अपर्याप्त रीसेटिंग सूचना के रिसाव का कारण बन सकती है। गोपनीय डेटा वाली वस्तुओं (जैसे उपयोगकर्ता के क्रेडिट कार्ड नंबर) को नए ग्राहकों को पास करने से पहले साफ़ किया जाना चाहिए, अन्यथा, डेटा को अनधिकृत पार्टी के सामने प्रकट किया जा सकता है।
यदि पूल का उपयोग कई थ्रेड्स द्वारा किया जाता है, तो समानांतर थ्रेड्स को उसी ऑब्जेक्ट को समानांतर में पुन: उपयोग करने से रोकने के लिए साधनों की आवश्यकता हो सकती है। यह आवश्यक नहीं है यदि पूल किए गए ऑब्जेक्ट अपरिवर्तनीय हैं या अन्यथा थ्रेड-सुरक्षित हैं।
आलोचना
कुछ प्रकाशन कुछ भाषाओं, जैसे जावा (प्रोग्रामिंग भाषा) के साथ ऑब्जेक्ट पूलिंग का उपयोग करने की अनुशंसा नहीं करते हैं, विशेष रूप से उन वस्तुओं के लिए जो केवल मेमोरी का उपयोग करते हैं और कोई बाहरी संसाधन नहीं रखते हैं (जैसे डेटाबेस से कनेक्शन)। विरोधी आमतौर पर कहते हैं कि कचरा संग्रह (कंप्यूटर विज्ञान) के साथ आधुनिक भाषाओं में वस्तु आवंटन अपेक्षाकृत तेज़ है; जबकि ऑपरेटर new
केवल दस निर्देशों की जरूरत है, क्लासिक new
- delete
पूलिंग डिज़ाइन में पाई जाने वाली जोड़ी को उनमें से सैकड़ों की आवश्यकता होती है क्योंकि यह अधिक जटिल कार्य करता है। इसके अलावा, अधिकांश कचरा संग्राहक लाइव ऑब्जेक्ट संदर्भों को स्कैन करते हैं, न कि उस मेमोरी को जो ये ऑब्जेक्ट अपनी सामग्री के लिए उपयोग करते हैं। इसका तात्पर्य यह है कि संदर्भ के बिना किसी भी संख्या में मृत वस्तुओं को बहुत कम लागत के साथ त्याग दिया जा सकता है। इसके विपरीत, बड़ी संख्या में जीवित लेकिन अप्रयुक्त वस्तुओं को रखने से कचरा संग्रह की अवधि बढ़ जाती है।[1]
उदाहरण
जाओ
निम्नलिखित गो कोड चैनलों के माध्यम से संसाधन दौड़ के मुद्दों से बचने के लिए एक निर्दिष्ट आकार (समवर्ती आरंभीकरण) के एक संसाधन पूल को आरंभीकृत करता है, और एक खाली पूल के स्थितिे में, क्लाइंट को बहुत लंबे समय तक प्रतीक्षा करने से रोकने के लिए टाइमआउट प्रोसेसिंग सेट करता है।
// package pool
package pool
import (
"errors"
"log"
"math/rand"
"sync"
"time"
)
const getResMaxTime = 3 * time.Second
var (
ErrPoolNotExist = errors.New("pool not exist")
ErrGetResTimeout = errors.New("get resource time out")
)
//Resource
type Resource struct {
resId int
}
//NewResource Simulate slow resource initialization creation
// (e.g., TCP connection, SSL symmetric key acquisition, auth authentication are time-consuming)
func NewResource(id int) *Resource {
time.Sleep(500 * time.Millisecond)
return &Resource{resId: id}
}
//Do Simulation resources are time consuming and random consumption is 0~400ms
func (r *Resource) Do(workId int) {
time.Sleep(time.Duration(rand.Intn(5)) * 100 * time.Millisecond)
log.Printf("using resource #%d finished work %d finish\n", r.resId, workId)
}
//Pool based on Go channel implementation, to avoid resource race state problem
type Pool chan *Resource
//New a resource pool of the specified size
// Resources are created concurrently to save resource initialization time
func New(size int) Pool {
p := make(Pool, size)
wg := new(sync.WaitGroup)
wg.Add(size)
for i := 0; i < size; i++ {
go func(resId int) {
p <- NewResource(resId)
wg.Done()
}(i)
}
wg.Wait()
return p
}
//GetResource based on channel, resource race state is avoided and resource acquisition timeout is set for empty pool
func (p Pool) GetResource() (r *Resource, err error) {
select {
case r := <-p:
return r, nil
case <-time.After(getResMaxTime):
return nil, ErrGetResTimeout
}
}
//GiveBackResource returns resources to the resource pool
func (p Pool) GiveBackResource(r *Resource) error {
if p == nil {
return ErrPoolNotExist
}
p <- r
return nil
}
// package main
package main
import (
"github.com/tkstorm/go-design/creational/object-pool/pool"
"log"
"sync"
)
func main() {
// Initialize a pool of five resources,
// which can be adjusted to 1 or 10 to see the difference
size := 5
p := pool.New(size)
// Invokes a resource to do the id job
doWork := func(workId int, wg *sync.WaitGroup) {
defer wg.Done()
// Get the resource from the resource pool
res, err := p.GetResource()
if err != nil {
log.Println(err)
return
}
// Resources to return
defer p.GiveBackResource(res)
// Use resources to handle work
res.Do(workId)
}
// Simulate 100 concurrent processes to get resources from the asset pool
num := 100
wg := new(sync.WaitGroup)
wg.Add(num)
for i := 0; i < num; i++ {
go doWork(i, wg)
}
wg.Wait()
}
सी #
.NET बेस क्लास लाइब्रेरी में कुछ ऑब्जेक्ट हैं जो इस पैटर्न को लागू करते हैं। System.Threading.ThreadPool
आवंटित करने के लिए धागे की पूर्वनिर्धारित संख्या के लिए कॉन्फ़िगर किया गया है। जब धागे लौटाए जाते हैं, तो वे दूसरी गणना के लिए उपलब्ध होते हैं। इस प्रकार, थ्रेड्स के निर्माण और निपटान की लागत का भुगतान किए बिना थ्रेड्स का उपयोग किया जा सकता है।
निम्नलिखित सी # का उपयोग करके कार्यान्वित ऑब्जेक्ट पूल डिज़ाइन पैटर्न का मूल कोड दिखाता है। संक्षिप्तता के लिए कक्षाओं के गुणों को सी # 3.0 स्वचालित रूप से कार्यान्वित संपत्ति सिंटैक्स का उपयोग करके घोषित किया जाता है। इन्हें भाषा के पुराने संस्करणों के लिए पूर्ण संपत्ति परिभाषाओं से बदला जा सकता है। पूल को एक स्थिर वर्ग के रूप में दिखाया गया है, क्योंकि कई पूलों की आवश्यकता होना असामान्य है। हालाँकि, ऑब्जेक्ट पूल के लिए इंस्टेंस क्लास का उपयोग करना समान रूप से स्वीकार्य है।
namespace DesignPattern.Objectpool;
// The PooledObject class is the type that is expensive or slow to instantiate,
// or that has limited availability, so is to be held in the object pool.
public class PooledObject
{
private DateTime _createdAt = DateTime.Now;
public DateTime CreatedAt
{
get { return _createdAt; }
}
public string TempData { get; set; }
}
// The Pool class controls access to the pooled objects. It maintains a list of available objects and a
// collection of objects that have been obtained from the pool and are in use. The pool ensures that released objects
// are returned to a suitable state, ready for reuse.
public static class Pool
{
private static List<PooledObject> _available = new List<PooledObject>();
private static List<PooledObject> _inUse = new List<PooledObject>();
public static PooledObject GetObject()
{
lock (_available)
{
if (_available.Count != 0)
{
PooledObject po = _available[0];
_inUse.Add(po);
_available.RemoveAt(0);
return po;
}
else
{
PooledObject po = new PooledObject();
_inUse.Add(po);
return po;
}
}
}
public static void ReleaseObject(PooledObject po)
{
CleanUp(po);
lock (_available)
{
_available.Add(po);
_inUse.Remove(po);
}
}
private static void CleanUp(PooledObject po)
{
po.TempData = null;
}
}
ऊपर दिए गए कोड में, पूलेडऑब्जेक्ट में उस समय के लिए गुण होते हैं जब इसे बनाया गया था, और दूसरा, जिसे क्लाइंट द्वारा संशोधित किया जा सकता है, जिसे पूल में वापस जारी किए जाने पर रीसेट किया जाता है। दिखाया गया है क्लीन-अप प्रक्रिया, किसी वस्तु को जारी करने पर, यह सुनिश्चित करने से पहले कि पूल से फिर से अनुरोध किया जा सकता है, यह एक वैध स्थिति में है।
जावा
जावा थ्रेड पूल पैटर्न का समर्थन करता है java.util.concurrent.ExecutorService
और अन्य संबंधित वर्ग। निष्पादक सेवा में एक निश्चित संख्या में मूल सूत्र होते हैं जिन्हें कभी नहीं छोड़ा जाता है। यदि सभी धागे व्यस्त हैं, तो सेवा अतिरिक्त धागे की अनुमत संख्या आवंटित करती है जिन्हें बाद में छोड़ दिया जाता है यदि निश्चित समाप्ति समय के लिए उपयोग नहीं किया जाता है। यदि अधिक थ्रेड्स की स्वीकृति नहीं है, तो कार्यों को कतार में रखा जा सकता है। अंत में, यदि यह कतार बहुत लंबी हो सकती है, तो अनुरोध करने वाले थ्रेड को निलंबित करने के लिए इसे कॉन्फ़िगर किया जा सकता है।
public class PooledObject {
public String temp1;
public String temp2;
public String temp3;
public String getTemp1() {
return temp1;
}
public void setTemp1(String temp1) {
this.temp1 = temp1;
}
public String getTemp2() {
return temp2;
}
public void setTemp2(String temp2) {
this.temp2 = temp2;
}
public String getTemp3() {
return temp3;
}
public void setTemp3(String temp3) {
this.temp3 = temp3;
}
}
public class PooledObjectPool {
private static long expTime = 6000;//6 seconds
public static HashMap<PooledObject, Long> available = new HashMap<PooledObject, Long>();
public static HashMap<PooledObject, Long> inUse = new HashMap<PooledObject, Long>();
public synchronized static PooledObject getObject() {
long now = System.currentTimeMillis();
if (!available.isEmpty()) {
for (Map.Entry<PooledObject, Long> entry : available.entrySet()) {
if (now - entry.getValue() > expTime) { //object has expired
popElement(available);
} else {
PooledObject po = popElement(available, entry.getKey());
push(inUse, po, now);
return po;
}
}
}
// either no PooledObject is available or each has expired, so return a new one
return createPooledObject(now);
}
private synchronized static PooledObject createPooledObject(long now) {
PooledObject po = new PooledObject();
push(inUse, po, now);
return po;
}
private synchronized static void push(HashMap<PooledObject, Long> map,
PooledObject po, long now) {
map.put(po, now);
}
public static void releaseObject(PooledObject po) {
cleanUp(po);
available.put(po, System.currentTimeMillis());
inUse.remove(po);
}
private static PooledObject popElement(HashMap<PooledObject, Long> map) {
Map.Entry<PooledObject, Long> entry = map.entrySet().iterator().next();
PooledObject key= entry.getKey();
//Long value=entry.getValue();
map.remove(entry.getKey());
return key;
}
private static PooledObject popElement(HashMap<PooledObject, Long> map, PooledObject key) {
map.remove(key);
return key;
}
public static void cleanUp(PooledObject po) {
po.setTemp1(null);
po.setTemp2(null);
po.setTemp3(null);
}
}
यह भी देखें
- कनेक्शन पूल
- मुफ्त सूची
- स्लैब आवंटन
टिप्पणियाँ
- ↑ 1.0 1.1 Goetz, Brian (2005-09-27). "Java theory and practice: Urban performance legends, revisited". IBM. IBM developerWorks. Archived from the original on 2012-02-14. Retrieved 2021-03-15.
संदर्भ
- Kircher, Michael; Prashant Jain (2002-07-04). "Pooling Pattern" (PDF). EuroPLoP 2002. Germany. Retrieved 2007-06-09.
- Goldshtein, Sasha; Zurbalev, Dima; Flatow, Ido (2012). Pro .NET Performance: Optimize Your C# Applications. Apress. ISBN 978-1-4302-4458-5.