ناشناس

سوال 2351

در مورد دیزاین پترن Object Pool چه می دانید؟

ثبت پاسخ جدید

برای ثبت پاسخ، لطفا وارد حساب کاربری خود شوید.

پاسخ‌ها

1 پاسخ
اگه بخوام تو یه خط توضیحش بدم:
این الگوی طراحی رو توی موقعیت هایی استفاده میکنیم که زمان ایجاد یک آبجکت خیلی بالا باشهمزایای این الگو چی هستش؟
تاثیر خیلی زیادی روی Performance داره
قابلیت استفاده مجدد از آبجکت های ایجاد شده و Share کردن آبجکت رو در زمان اجرای برنامه به ما میده
کی از این الگو استفاده کنیم؟
در صورتی که توی کد خودتون جایی دیدید که از یک نوع آبجکت تعداد زیادی new و Dispose میکنید
در صورتی که بدونیم تعداد محدودی از آبجکت ها داریم که همزمان با هم استفاده میشندیک مثالی که برنامه نویس های دات نت و جاوا خیلی باهاش درگیر هستن ایجاد آبجکتی برای اتصال به دیتابیس هستش, در صورتی که برنامه ما اتصال به دیتابیس زیادی داشته باشه ایجاد connection جدید و از بین بردن اون شدیدا روی کارایی برنامه تاثیر منفی میزاره و توان سیستم در پردازش تعداد درخواست همزمان رو به شدت پایین میاره و موضوع دوم هم اگر ما همزمان connection های باز زیادی داشته باشیم یک سربار به دیتابیس اضافه میشه که باعث میشه تا دیتابیس هم تحت تاثیر قرار بگیره.
برای حل این مشکل توی سی شارپ از کلاسی مثل زیر استفاده میکنیم:
public class ObjectPool<T>
    {
        private ConcurrentBag<T> _objects;
        private Func<T> _objectGenerator;
        public ObjectPool(Func<T> objectGenerator)
        {
            if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
            _objects = new ConcurrentBag<T>();
            _objectGenerator = objectGenerator;
        }
        public T GetObject()
        {
            T item;
            if (_objects.TryTake(out item)) return item;
            return _objectGenerator();
        }
        public void PutObject(T item)
        {
            _objects.Add(item);
        }
    }
دلیل استفاده از ConcurrentBag این هستش که میخوایم به صورت Parallel کد رو اجرا کنیم. در اینجا صرفا یه لیست ایجاد کردیم و زمانی که نیاز به یه آبجکت داشته باشیم و در لیست وجود نداشته باشه یه آبجکت جدید ایجاد میکنیم و وقتی کارمون با آبجکت تموم شد توی لیست قرارش میدیم تا درخواست بعدی از آبجکت استفاده کنه.
برای این که تاثیر این الگو رو ببیند من سعی کردم تا کلاسی که ایجاد میکنم مصرف مموری بالایی داشته باشه:
class MyClass
    {
        public int[] Nums { get; set; }
        public long GetValue(long i)
        {
            return Nums[i];
        }
        public MyClass()
        {
            Nums = new int[100000];
            Random rand = new Random();
            for (int i = 0; i < Nums.Length; i++)
                Nums[i] = rand.Next();
        }
    }
همون طوی که میبینید این کلاس هم مصرف حافظه بالایی داره هم زمان ایجاد یک آبجکت جدید از این کلاس زمان زیادی رو میگیره(روی سیستم من چیزی در حدود 4 میلی ثانیه), خب بریم سراغ کد اصلی که میخواد از ترکیب این دو استفاده کنه:
           ObjectPool<MyClass> pool = new ObjectPool<MyClass>(() => new MyClass());
            Parallel.For(0, 10000, (i, loopState) =>
            {
                MyClass mc = pool.GetObject();
                Console.CursorLeft = 0;
                Console.WriteLine(mc.GetValue(i));
                pool.PutObject(mc);
            });
ده هزار آبجکت رو به صورت همزمان توی ترد های مختلف اجرا میکنیم و سعی میکنم از object pool استفاده کنیم, برای این که تاثیر این کار رو ببینم سعی میکنم همین تعداد آبجکت رو در حالت عادی هم ایجاد کنیم:
for (int i = 0; i <= 10000; i++)
            {
                MyClass mc = new MyClass();
                Console.WriteLine(mc.GetValue(i));
            }
من این دو تا کد رو اجرا کردم و میزان مصرف حافظه هر دو رو با همدیگه مقایسه کردم اگر دوست داشتین خودتون میتونیم زمان اجرا رو هم خودتون مقایسه کنید: